import {
  createEffect,
  createMemo,
  createSignal,
  createUniqueId,
  onMount,
  Show,
  type Component,
  type JSX,
  type Ref,
} from "solid-js";
import Scaffold from "../material/Scaffold";
import {
  Avatar,
  Button,
  IconButton,
  List,
  ListItemButton,
  ListItemIcon,
  ListItemSecondaryAction,
  ListItemText,
  Radio,
  Switch,
  Divider,
  CircularProgress,
} from "@suid/material";
import {
  ArrowDropDown,
  Public as PublicIcon,
  Send,
  People as PeopleIcon,
  ThreeP as ThreePIcon,
  ListAlt as ListAltIcon,
  Visibility,
  Translate,
} from "@suid/icons-material";
import type { Account } from "../accounts/stores";
import tootComposers from "./TootComposer.module.css";
import { makeEventListener } from "@solid-primitives/event-listener";
import BottomSheet from "../material/BottomSheet";
import { useLanguage } from "../platform/i18n";
import iso639_1 from "iso-639-1";
import ChooseTootLang from "./ChooseTootLang";
import type { mastodon } from "masto";

type TootVisibility = "public" | "unlisted" | "private" | "direct";

const TootVisibilityPickerDialog: Component<{
  open?: boolean;
  onClose: () => void;
  visibility: TootVisibility;
  onVisibilityChange: (value: TootVisibility) => void;
}> = (props) => {
  type Kind = "public" | "private" | "direct";

  const kind = () =>
    props.visibility === "public" || props.visibility === "unlisted"
      ? "public"
      : props.visibility;

  const setKind = (nv: Kind) => {
    if (nv == "public") {
      props.onVisibilityChange(discoverable() ? "public" : "unlisted");
    } else {
      props.onVisibilityChange(nv);
    }
  };

  const discoverable = () => {
    return props.visibility === "public";
  };

  const setDiscoverable = (setter: (v: boolean) => boolean) => {
    const nval = setter(discoverable());
    props.onVisibilityChange(nval ? "public" : "unlisted"); // trigger change
  };

  return (
    <BottomSheet open={props.open} onClose={props.onClose} bottomUp>
      <Scaffold
        bottom={
          <div
            style={{
              "border-top": "1px solid #ddd",
              background: "var(--tutu-color-surface)",
              padding: "8px 16px",
              width: "100%",
              "text-align": "end",
            }}
          >
            <Button onClick={props.onClose}>Confirm</Button>
          </div>
        }
      >
        <List dense>
          <ListItemButton onClick={[setKind, "public"]}>
            <ListItemIcon>
              <PublicIcon />
            </ListItemIcon>
            <ListItemText
              primary="Public"
              secondary="Everyone can see this toot"
            ></ListItemText>
            <ListItemSecondaryAction>
              <Radio checked={kind() == "public"}></Radio>
            </ListItemSecondaryAction>
          </ListItemButton>

          <ListItemButton
            sx={{ paddingLeft: "40px" }}
            disabled={kind() !== "public"}
            onClick={() => setDiscoverable((x) => !x)}
          >
            <ListItemIcon>
              <ListAltIcon />
            </ListItemIcon>
            <ListItemText
              primary="Discoverable"
              secondary="The others can discover it on the exploration."
            ></ListItemText>
            <ListItemSecondaryAction>
              <Switch
                checked={discoverable()}
                disabled={kind() !== "public"}
              ></Switch>
            </ListItemSecondaryAction>
          </ListItemButton>

          <Divider />

          <ListItemButton onClick={[setKind, "private"]}>
            <ListItemIcon>
              <PeopleIcon />
            </ListItemIcon>
            <ListItemText
              primary="Only Followers"
              secondary="Visibile for followers only"
            ></ListItemText>
            <ListItemSecondaryAction>
              <Radio checked={kind() == "private"}></Radio>
            </ListItemSecondaryAction>
          </ListItemButton>

          <Divider />

          <ListItemButton onClick={[setKind, "direct"]}>
            <ListItemIcon>
              <ThreePIcon />
            </ListItemIcon>
            <ListItemText
              primary="Only Mentions"
              secondary="Visible for mentioned users only"
            ></ListItemText>
            <ListItemSecondaryAction>
              <Radio checked={kind() == "direct"}></Radio>
            </ListItemSecondaryAction>
          </ListItemButton>
        </List>
      </Scaffold>
    </BottomSheet>
  );
};

const TootLanguagePickerDialog: Component<{
  open?: boolean;
  onClose: () => void;
  code: string;
  onCodeChange: (nval: string) => void;
}> = (props) => {
  return (
    <BottomSheet open={props.open} onClose={props.onClose}>
      <Show when={props.open}>
        <ChooseTootLang
          code={props.code}
          onCodeChange={props.onCodeChange}
          onClose={props.onClose}
        />
      </Show>
    </BottomSheet>
  );
};

function randomChoose<T extends any[]>(
  rn: number,
  K: T,
): T extends Array<infer E> ? E : never {
  const idx = Math.floor(rn * K.length);
  return K[idx];
}

const TootComposer: Component<{
  ref?: Ref<HTMLDivElement>;
  style?: JSX.CSSProperties;
  profile?: Account;
  replyToDisplayName?: string;
  mentions?: readonly string[];
  isTyping?: boolean;
  onTypingChange: (value: boolean) => void;
  client?: mastodon.rest.Client;
  inReplyToId?: string;
  onSent?: (status: mastodon.v1.Status) => void;
}> = (props) => {
  let inputRef: HTMLTextAreaElement;
  let sendKey: string | undefined;

  const typing = () => props.isTyping;
  const setTyping = (v: boolean) => props.onTypingChange(v);
  const [sending, setSending] = createSignal(false);
  const [visibility, setVisibility] = createSignal<TootVisibility>("public");
  const [permPicker, setPermPicker] = createSignal(false);
  const [language, setLanguage] = createSignal("en");
  const [langPickerOpen, setLangPickerOpen] = createSignal(false);
  const appLanguage = useLanguage();

  const randomPlaceholder = createMemo(() =>
    randomChoose(Math.random(), ["What's happening?", "What do your think?"]),
  );

  createEffect(() => {
    const lang = appLanguage().split("-")[0];
    setLanguage(lang);
  });

  createEffect(() => {
    if (typing()) {
      setTimeout(() => inputRef.focus(), 0);
    }
  });

  onMount(() => {
    makeEventListener(inputRef, "focus", () => setTyping(true));
  });

  createEffect(() => {
    if (inputRef.value !== "") return;
    if (props.mentions) {
      const prepText = props.mentions.join(" ") + " ";
      inputRef.value = prepText;
    }
  });

  const containerStyle = () =>
    typing() || permPicker()
      ? {
          position: "sticky" as const,
          top: "var(--scaffold-topbar-height, 0)",
          bottom: "var(--safe-area-inset-bottom, 0)",
          "z-index": 2,
          ...props.style,
        }
      : undefined;

  const visibilityText = () => {
    switch (visibility()) {
      case "public":
        return "Discoverable";
      case "unlisted":
        return "Public";
      case "private":
        return "Only Followers";
      case "direct":
        return "Only Mentions";
    }
  };

  const getOrGenSendKey = () => {
    if (sendKey === undefined) {
      sendKey = window.crypto.randomUUID();
    }
    return sendKey;
  };

  const send = async () => {
    setSending(true);
    try {
      const status = await props.client!.v1.statuses.create(
        {
          status: inputRef.value,
          language: language(),
          visibility: visibility(),
          inReplyToId: props.inReplyToId,
        },
        {
          requestInit: {
            headers: {
              ["Idempotency-Key"]: getOrGenSendKey(),
            },
          },
        },
      );

      props.onSent?.(status);
      inputRef.value = "";
    } finally {
      setSending(false);
    }
  };

  return (
    <div
      ref={props.ref}
      class={tootComposers.composer}
      style={containerStyle()}
      onClick={(e) => inputRef.focus()}
    >
      <div class={tootComposers.replyInput}>
        <Show when={props.profile}>
          <Avatar
            src={props.profile!.inf?.avatar}
            sx={{ marginLeft: "-0.25em" }}
          />
        </Show>
        <textarea
          ref={inputRef!}
          placeholder={
            props.replyToDisplayName
              ? `Reply to ${props.replyToDisplayName}...`
              : randomPlaceholder()
          }
          style={{ width: "100%", border: "none" }}
          disabled={sending()}
        ></textarea>
        <Show when={props.client}>
          <Show
            when={!sending()}
            fallback={
              <div style={{ padding: "8px" }}>
                <CircularProgress
                  sx={{
                    marginRight: "-0.5em",
                    width: "1.5rem",
                    height: "1.5rem",
                  }}
                />
              </div>
            }
          >
            <IconButton sx={{ marginRight: "-0.5em" }} onClick={send}>
              <Send />
            </IconButton>
          </Show>
        </Show>
      </div>

      <Show when={typing()}>
        <div
          style={{
            display: "flex",
            "justify-content": "flex-end",
            "margin-top": "8px",
            gap: "16px",
            "flex-flow": "row wrap",
          }}
        >
          <Button onClick={[setLangPickerOpen, true]} disabled={sending()}>
            <Translate sx={{ marginTop: "-0.25em", marginRight: "0.25em" }} />
            {iso639_1.getNativeName(language())}
            <ArrowDropDown sx={{ marginTop: "-0.25em" }} />
          </Button>
          <Button onClick={[setPermPicker, true]} disabled={sending()}>
            <Visibility sx={{ marginTop: "-0.15em", marginRight: "0.25em" }} />
            {visibilityText()}
            <ArrowDropDown sx={{ marginTop: "-0.25em" }} />
          </Button>
        </div>

        <TootVisibilityPickerDialog
          open={permPicker()}
          onClose={() => setPermPicker(false)}
          visibility={visibility()}
          onVisibilityChange={setVisibility}
        />

        <TootLanguagePickerDialog
          open={langPickerOpen()}
          onClose={() => setLangPickerOpen(false)}
          code={language()}
          onCodeChange={setLanguage}
        />
      </Show>
    </div>
  );
};

export default TootComposer;
