import { useTranslation } from "react-i18next";
import {
  Card,
  CardContent,
  CardHeader,
  CardTitle,
} from "@/components/ui/card.tsx";
import Paragraph, {
  ParagraphDescription,
  ParagraphFooter,
  ParagraphItem,
  ParagraphSpace,
} from "@/components/Paragraph.tsx";
import { Button } from "@/components/ui/button.tsx";
import { Label } from "@/components/ui/label.tsx";
import { Input } from "@/components/ui/input.tsx";
import { useMemo, useReducer, useState } from "react";
import { formReducer } from "@/utils/form.ts";
import { NumberInput } from "@/components/ui/number-input.tsx";
import {
  CommonState,
  commonWhiteList,
  GeneralState,
  getConfig,
  initialSystemState,
  MailState,
  SearchState,
  setConfig,
  SiteState,
  SystemProps,
  updateRootPassword,
} from "@/admin/api/system.ts";
import { useEffectAsync } from "@/utils/hook.ts";
import { toastState } from "@/api/common.ts";
import { toast, useToast } from "@/components/ui/use-toast.ts";
import { doVerify } from "@/api/auth.ts";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTrigger,
} from "@/components/ui/dialog.tsx";
import { DialogTitle } from "@radix-ui/react-dialog";
import Require from "@/components/Require.tsx";
import { PencilLine, RotateCw, Save, Settings2 } from "lucide-react";
import { FlexibleTextarea } from "@/components/ui/textarea.tsx";
import Tips from "@/components/Tips.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
import { Switch } from "@/components/ui/switch.tsx";
import { MultiCombobox } from "@/components/ui/multi-combobox.tsx";
import { allGroups } from "@/utils/groups.ts";
import { useChannelModels } from "@/admin/hook.tsx";
import { useSelector } from "react-redux";
import { selectSupportModels } from "@/store/chat.ts";
import { JSONEditorProvider } from "@/components/EditorProvider.tsx";

type CompProps<T> = {
  data: T;
  form: SystemProps;
  dispatch: (action: any) => void;
  onChange: (doToast?: boolean) => Promise<void>;
};

function RootDialog() {
  const { t } = useTranslation();
  const { toast } = useToast();
  const [open, setOpen] = useState<boolean>(false);
  const [password, setPassword] = useState<string>("");
  const [repeat, setRepeat] = useState<string>("");

  const onPost = async () => {
    const res = await updateRootPassword(password);
    toastState(toast, t, res, true);
    if (res.status) {
      setPassword("");
      setRepeat("");
      setOpen(false);

      setTimeout(() => {
        window.location.reload();
      }, 1000);
    }
  };

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        <Button variant={`outline`} size={`sm`}>
          {t("admin.system.updateRoot")}
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>{t("admin.system.updateRoot")}</DialogTitle>
          <DialogDescription>
            <div className={`mb-4 select-none`}>
              {t("admin.system.updateRootTip")}
            </div>
            <Input
              className={`mb-2`}
              type={`password`}
              placeholder={t("admin.system.updateRootPlaceholder")}
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
            <Input
              type={`password`}
              placeholder={t("admin.system.updateRootRepeatPlaceholder")}
              value={repeat}
              onChange={(e) => setRepeat(e.target.value)}
            />
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <Button
            variant={`outline`}
            onClick={() => {
              setPassword("");
              setRepeat("");
              setOpen(false);
            }}
          >
            {t("admin.cancel")}
          </Button>
          <Button
            variant={`default`}
            loading={true}
            onClick={onPost}
            disabled={
              password.trim().length === 0 || password.trim() !== repeat.trim()
            }
          >
            {t("admin.confirm")}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function General({ data, dispatch, onChange }: CompProps<GeneralState>) {
  const { t } = useTranslation();

  return (
    <Paragraph
      title={t("admin.system.general")}
      configParagraph={true}
      isCollapsed={true}
    >
      <ParagraphItem>
        <Label>{t("admin.system.title")}</Label>
        <Input
          value={data.title}
          onChange={(e) =>
            dispatch({
              type: "update:general.title",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.titleTip")}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>{t("admin.system.docs")}</Label>
        <Input
          value={data.docs}
          onChange={(e) =>
            dispatch({
              type: "update:general.docs",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.docsTip")}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>{t("admin.system.logo")}</Label>
        <Input
          value={data.logo}
          onChange={(e) =>
            dispatch({
              type: "update:general.logo",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.logoTip", {
            logo: `${window.location.protocol}//${window.location.host}/favicon.ico`,
          })}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>{t("admin.system.backend")}</Label>
        <Input
          value={data.backend}
          onChange={(e) =>
            dispatch({
              type: "update:general.backend",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.backendPlaceholder", {
            backend: `${window.location.protocol}//${window.location.host}/api`,
          })}
        />
      </ParagraphItem>
      <ParagraphDescription>
        {t("admin.system.backendTip")}
      </ParagraphDescription>
      <ParagraphItem>
        <Label>{t("admin.system.file")}</Label>
        <Input
          value={data.file}
          onChange={(e) =>
            dispatch({
              type: "update:general.file",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.filePlaceholder")}
        />
      </ParagraphItem>
      <ParagraphDescription>{t("admin.system.fileTip")}</ParagraphDescription>
      <ParagraphItem>
        <Label>PWA Manifest</Label>
        <JSONEditorProvider
          value={data.pwa_manifest ?? ""}
          onChange={(value) =>
            dispatch({ type: "update:general.pwa_manifest", value })
          }
        >
          <Button variant={`outline`}>
            <PencilLine className={`h-4 w-4 mr-1`} />
            {t("edit")}
          </Button>
        </JSONEditorProvider>
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          {t("admin.system.debugMode")}
          <Tips
            className={`inline-block`}
            content={t("admin.system.debugModeTip")}
          />
        </Label>
        <Switch
          checked={data.debug_mode}
          onCheckedChange={(value) => {
            dispatch({ type: "update:general.debug_mode", value });
          }}
        />
      </ParagraphItem>
      <ParagraphSpace />
      <ParagraphFooter>
        <div className={`grow`} />
        <RootDialog />
        <Button
          size={`sm`}
          loading={true}
          onClick={async () => await onChange()}
        >
          {t("admin.system.save")}
        </Button>
      </ParagraphFooter>
    </Paragraph>
  );
}

function Mail({ data, dispatch, onChange }: CompProps<MailState>) {
  const { t } = useTranslation();
  const [email, setEmail] = useState<string>("");

  const [mailDialog, setMailDialog] = useState<boolean>(false);

  const valid = useMemo((): boolean => {
    return (
      data.host.length > 0 &&
      data.port > 0 &&
      data.port < 65535 &&
      data.username.length > 0 &&
      data.password.length > 0 &&
      data.from.length > 0 &&
      /\w+@\w+\.\w+/.test(data.from) &&
      !data.username.includes("@")
    );
  }, [data]);

  const onTest = async () => {
    if (!email.trim()) return;
    await onChange(false);
    const res = await doVerify(email);
    toastState(toast, t, res, true);

    if (res.status) setMailDialog(false);
  };

  const white_list = useMemo(() => {
    const raw = data.white_list.custom
      .split(",")
      .map((item) => item.trim())
      .filter((item) => item.length > 0);

    return [...commonWhiteList, ...raw];
  }, [data]);

  return (
    <Paragraph
      title={t("admin.system.mail")}
      configParagraph={true}
      isCollapsed={true}
    >
      {!valid && (
        <ParagraphDescription border={true}>
          {t("admin.system.mailConfNotValid")}
        </ParagraphDescription>
      )}
      <ParagraphItem>
        <Label>
          <Require /> {t("admin.system.mailHost")}
        </Label>
        <Input
          value={data.host}
          onChange={(e) =>
            dispatch({
              type: "update:mail.host",
              value: e.target.value,
            })
          }
          placeholder={`smtp.qcloudmail.com`}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          <Require /> {t("admin.system.mailPort")}
        </Label>
        <NumberInput
          value={data.port}
          onValueChange={(value) =>
            dispatch({ type: "update:mail.port", value })
          }
          placeholder={`465`}
          min={0}
          max={65535}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          <Require /> {t("admin.system.mailUser")}
        </Label>
        <Input
          value={data.username}
          onChange={(e) =>
            dispatch({
              type: "update:mail.username",
              value: e.target.value,
            })
          }
          className={cn(
            "transition-all duration-300",
            data.username.includes("@") && `border-red-700`,
          )}
          placeholder={t("admin.system.mailUser")}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          <Require /> {t("admin.system.mailPass")}
        </Label>
        <Input
          value={data.password}
          onChange={(e) =>
            dispatch({
              type: "update:mail.password",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.mailPass")}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          <Require /> {t("admin.system.mailFrom")}
        </Label>
        <Input
          value={data.from}
          onChange={(e) =>
            dispatch({
              type: "update:mail.from",
              value: e.target.value,
            })
          }
          placeholder={`${data.username}@${location.hostname}`}
          className={cn(
            "transition-all duration-300",
            data.from.length > 0 &&
              !/\w+@\w+\.\w+/.test(data.from) &&
              `border-red-700`,
          )}
        />
      </ParagraphItem>
      <ParagraphSpace />
      <ParagraphItem>
        <Label>{t("admin.system.mailEnableWhitelist")}</Label>
        <Switch
          checked={data.white_list.enabled}
          onCheckedChange={(value) => {
            dispatch({
              type: "update:mail.white_list.enabled",
              value,
            });
          }}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>{t("admin.system.mailWhitelist")}</Label>
        <MultiCombobox
          value={data.white_list.white_list}
          list={white_list}
          disabled={!data.white_list.enabled}
          onChange={(value) => {
            dispatch({
              type: "update:mail.white_list.white_list",
              value,
            });
          }}
          placeholder={t("admin.system.mailWhitelistSelected", {
            length: data.white_list.white_list.length,
          })}
          searchPlaceholder={t("admin.system.mailWhitelistSearchPlaceholder")}
        />
      </ParagraphItem>
      <Input
        className={`mb-2`}
        value={data.white_list.custom}
        onChange={(e) =>
          dispatch({
            type: "update:mail.white_list.custom",
            value: e.target.value,
          })
        }
        disabled={!data.white_list.enabled}
        placeholder={t("admin.system.customWhitelistPlaceholder")}
      />
      <ParagraphFooter>
        <div className={`grow`} />
        <Dialog open={mailDialog} onOpenChange={setMailDialog}>
          <DialogTrigger asChild>
            <Button variant={`outline`} size={`sm`}>
              {t("admin.system.test")}
            </Button>
          </DialogTrigger>
          <DialogContent>
            <DialogHeader>
              <DialogTitle>{t("admin.system.test")}</DialogTitle>
              <DialogDescription className={`pt-2`}>
                <Input
                  placeholder={t("auth.email-placeholder")}
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                />
              </DialogDescription>
            </DialogHeader>
            <DialogFooter>
              <Button
                variant={`outline`}
                onClick={() => {
                  setEmail("");
                  setMailDialog(false);
                }}
              >
                {t("admin.cancel")}
              </Button>
              <Button variant={`default`} loading={true} onClick={onTest}>
                {t("admin.confirm")}
              </Button>
            </DialogFooter>
          </DialogContent>
        </Dialog>
        <Button
          size={`sm`}
          loading={true}
          onClick={async () => await onChange()}
        >
          {t("admin.system.save")}
        </Button>
      </ParagraphFooter>
    </Paragraph>
  );
}

function Site({ data, dispatch, onChange }: CompProps<SiteState>) {
  const { t } = useTranslation();

  return (
    <Paragraph
      title={t("admin.system.site")}
      configParagraph={true}
      isCollapsed={true}
    >
      <ParagraphItem>
        <Label>
          {t("admin.system.closeRegistration")}
          <Tips
            className={`inline-block`}
            content={t("admin.system.closeRegistrationTip")}
          />
        </Label>
        <Switch
          checked={data.close_register}
          onCheckedChange={(value) => {
            dispatch({ type: "update:site.close_register", value });
          }}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          {t("admin.system.closeRelay")}
          <Tips
            className={`inline-block`}
            content={t("admin.system.closeRelayTip")}
          />
        </Label>
        <Switch
          checked={data.close_relay}
          onCheckedChange={(value) => {
            dispatch({ type: "update:site.close_relay", value });
          }}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          {t("admin.system.relayPlan")}
          <Tips
            className={`inline-block`}
            content={t("admin.system.relayPlanTip")}
          />
        </Label>
        <Switch
          checked={data.relay_plan}
          onCheckedChange={(value) => {
            dispatch({ type: "update:site.relay_plan", value });
          }}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label className={`flex flex-row items-center`}>
          {t("admin.system.quota")}
          <Tips content={t("admin.system.quotaTip")} />
        </Label>
        <NumberInput
          value={data.quota}
          onValueChange={(value) =>
            dispatch({ type: "update:site.quota", value })
          }
          placeholder={`5`}
          min={0}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>{t("admin.system.buyLink")}</Label>
        <Input
          value={data.buy_link}
          onChange={(e) =>
            dispatch({
              type: "update:site.buy_link",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.buyLinkPlaceholder")}
        />
      </ParagraphItem>
      <ParagraphItem rowLayout={true}>
        <Label>{t("admin.system.announcement")}</Label>
        <FlexibleTextarea
          value={data.announcement}
          rows={12}
          onChange={(e) =>
            dispatch({
              type: "update:site.announcement",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.announcementPlaceholder")}
        />
      </ParagraphItem>
      <ParagraphItem rowLayout={true}>
        <Label>{t("admin.system.contact")}</Label>
        <FlexibleTextarea
          value={data.contact}
          rows={6}
          onChange={(e) =>
            dispatch({
              type: "update:site.contact",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.contactPlaceholder")}
        />
      </ParagraphItem>
      <ParagraphSpace />
      <ParagraphItem rowLayout={true}>
        <Label>{t("admin.system.footer")}</Label>
        <FlexibleTextarea
          rows={6}
          value={data.footer}
          onChange={(e) =>
            dispatch({
              type: "update:site.footer",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.footerPlaceholder")}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>{t("admin.system.authFooter")}</Label>
        <Switch
          checked={data.auth_footer}
          onCheckedChange={(value) => {
            dispatch({ type: "update:site.auth_footer", value });
          }}
        />
      </ParagraphItem>
      <ParagraphFooter>
        <div className={`grow`} />
        <Button
          size={`sm`}
          loading={true}
          onClick={async () => await onChange()}
        >
          {t("admin.system.save")}
        </Button>
      </ParagraphFooter>
    </Paragraph>
  );
}

function Common({ form, data, dispatch, onChange }: CompProps<CommonState>) {
  const { t } = useTranslation();

  const { channelModels } = useChannelModels();
  const supportModels = useSelector(selectSupportModels);

  return (
    <Paragraph
      title={t("admin.system.common")}
      configParagraph={true}
      isCollapsed={true}
    >
      <ParagraphItem>
        <Label className={`flex flex-row items-center`}>
          {t("admin.system.image_store")}
          <Tips content={t("admin.system.image_storeTip")} />
        </Label>
        <Switch
          checked={data.image_store}
          onCheckedChange={(value) => {
            dispatch({ type: "update:common.image_store", value });
          }}
        />
      </ParagraphItem>
      {data.image_store && form.general.backend.length === 0 && (
        <ParagraphDescription border={true}>
          {t("admin.system.image_storeNoBackend")}
        </ParagraphDescription>
      )}
      <ParagraphSpace />
      <ParagraphItem>
        <Label className={`flex flex-row items-center`}>
          {t("admin.system.cache")}
          <Tips content={t("admin.system.cacheTip")} />
        </Label>
        <MultiCombobox
          value={data.cache}
          onChange={(value) => {
            dispatch({ type: "update:common.cache", value });
          }}
          list={channelModels}
          placeholder={t("admin.system.cachePlaceholder", {
            length: (data.cache ?? []).length,
          })}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          {t("admin.system.cacheExpired")}
          <Tips
            className={`inline-block`}
            content={t("admin.system.cacheExpiredTip")}
          />
        </Label>
        <NumberInput
          value={data.expire}
          onValueChange={(value) =>
            dispatch({ type: "update:common.expire", value })
          }
          min={0}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          {t("admin.system.cacheSize")}
          <Tips
            className={`inline-block`}
            content={t("admin.system.cacheSizeTip")}
          />
        </Label>
        <NumberInput
          value={data.size}
          onValueChange={(value) =>
            dispatch({ type: "update:common.size", value })
          }
          min={0}
        />
      </ParagraphItem>
      <ParagraphItem>
        <div className={`flex flex-row flex-wrap gap-2 ml-auto`}>
          <Button
            variant={`outline`}
            onClick={() => dispatch({ type: "update:common.cache", value: [] })}
          >
            <Settings2
              className={`inline-flex h-4 w-4 mr-2 translate-y-[1px]`}
            />
            {t("admin.system.cacheNone")}
          </Button>
          <Button
            variant={`outline`}
            onClick={() =>
              dispatch({
                type: "update:common.cache",
                value: supportModels
                  .filter((item) => item.free)
                  .map((item) => item.id),
              })
            }
          >
            <Settings2
              className={`inline-flex h-4 w-4 mr-2 translate-y-[1px]`}
            />
            {t("admin.system.cacheFree")}
          </Button>
          <Button
            variant={`outline`}
            onClick={() =>
              dispatch({ type: "update:common.cache", value: channelModels })
            }
          >
            <Settings2
              className={`inline-flex h-4 w-4 mr-2 translate-y-[1px]`}
            />
            {t("admin.system.cacheAll")}
          </Button>
        </div>
      </ParagraphItem>
      <ParagraphSpace />
      <ParagraphItem>
        <Label className={`flex flex-row items-center`}>
          {t("admin.system.article")}
          <Tips content={t("admin.system.articleTip")} />
        </Label>
        <MultiCombobox
          value={data.article}
          onChange={(value) => {
            dispatch({ type: "update:common.article", value });
          }}
          list={allGroups}
          listTranslate={`admin.channels.groups`}
          placeholder={t("admin.system.groupPlaceholder", {
            length: (data.article ?? []).length,
          })}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label className={`flex flex-row items-center`}>
          {t("admin.system.generate")}
          <Tips content={t("admin.system.generateTip")} />
        </Label>
        <MultiCombobox
          value={data.generation}
          onChange={(value) => {
            dispatch({ type: "update:common.generation", value });
          }}
          list={allGroups}
          listTranslate={`admin.channels.groups`}
          placeholder={t("admin.system.groupPlaceholder", {
            length: (data.generation ?? []).length,
          })}
        />
      </ParagraphItem>
      <ParagraphFooter>
        <div className={`grow`} />
        <Button
          size={`sm`}
          loading={true}
          onClick={async () => await onChange()}
        >
          {t("admin.system.save")}
        </Button>
      </ParagraphFooter>
    </Paragraph>
  );
}

function Search({ data, dispatch, onChange }: CompProps<SearchState>) {
  const { t } = useTranslation();

  return (
    <Paragraph
      title={t("admin.system.search")}
      configParagraph={true}
      isCollapsed={true}
    >
      <ParagraphItem>
        <Label>{t("admin.system.searchEndpoint")}</Label>
        <Input
          value={data.endpoint}
          onChange={(e) =>
            dispatch({
              type: "update:search.endpoint",
              value: e.target.value,
            })
          }
          placeholder={t("admin.system.searchPlaceholder")}
        />
      </ParagraphItem>
      <ParagraphItem>
        <Label>
          {t("admin.system.searchQuery")}
          <Tips
            className={`inline-block`}
            content={t("admin.system.searchQueryTip")}
          />
        </Label>
        <NumberInput
          value={data.query}
          onValueChange={(value) =>
            dispatch({ type: "update:search.query", value })
          }
          placeholder={`5`}
          min={0}
          max={50}
        />
      </ParagraphItem>
      <ParagraphDescription>{t("admin.system.searchTip")}</ParagraphDescription>
      <ParagraphFooter>
        <div className={`grow`} />
        <Button
          size={`sm`}
          loading={true}
          onClick={async () => await onChange()}
        >
          {t("admin.system.save")}
        </Button>
      </ParagraphFooter>
    </Paragraph>
  );
}

function System() {
  const { t } = useTranslation();
  const { toast } = useToast();
  const [data, setData] = useReducer(
    formReducer<SystemProps>(),
    initialSystemState,
  );

  const [loading, setLoading] = useState<boolean>(false);

  const doSaving = async (doToast?: boolean) => {
    const res = await setConfig(data);

    if (doToast !== false) toastState(toast, t, res, true);
  };

  const doRefresh = async () => {
    setLoading(true);
    const res = await getConfig();
    setLoading(false);
    toastState(toast, t, res);
    if (res.status) {
      setData({ type: "set", value: res.data });
    }
  };

  useEffectAsync(doRefresh, []);

  return (
    <div className={`system`}>
      <Card className={`admin-card system-card`}>
        <CardHeader className={`select-none`}>
          <CardTitle>{t("admin.settings")}</CardTitle>
        </CardHeader>
        <CardContent className={`flex flex-col gap-1`}>
          <div className={`system-actions flex flex-row`}>
            <div className={`grow`} />
            <Button
              size={`icon`}
              variant={`outline`}
              loading={true}
              className={`mr-2`}
              onClick={async () => await doRefresh()}
            >
              <RotateCw className={cn(loading && `animate-spin`, `h-4 w-4`)} />
            </Button>
            <Button
              size={`icon`}
              loading={true}
              onClick={async () => await doSaving()}
            >
              <Save className={`h-4 w-4`} />
            </Button>
          </div>
          <General
            form={data}
            data={data.general}
            dispatch={setData}
            onChange={doSaving}
          />
          <Site
            form={data}
            data={data.site}
            dispatch={setData}
            onChange={doSaving}
          />
          <Mail
            form={data}
            data={data.mail}
            dispatch={setData}
            onChange={doSaving}
          />
          <Search
            form={data}
            data={data.search}
            dispatch={setData}
            onChange={doSaving}
          />
          <Common
            form={data}
            data={data.common}
            dispatch={setData}
            onChange={doSaving}
          />
        </CardContent>
      </Card>
    </div>
  );
}

export default System;
