"use client";

import { FormEvent, useMemo, useState } from "react";
import { Bell, CheckCircle2, FileText, Search, Undo2 } from "lucide-react";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Dialog } from "@/components/ui/dialog";
import { Input, Textarea } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import { Table } from "@/components/ui/table";
import type { ApiResult } from "@/modules/core/server/api";
import type { Notice, NoticeCenterData, NoticeInput, NoticeStatus } from "@/modules/notices/types";
import { NOTICE_STATUS_LABELS, NOTICE_STATUS_VALUES } from "@/modules/notices/types";

type NoticesWorkspaceClientProps = {
  canManage: boolean;
  initialData: NoticeCenterData;
};

type DialogState = { mode: "create"; value: NoticeInput } | { mode: "edit"; id: string; value: NoticeInput };

const emptyNotice: NoticeInput = {
  title: "",
  content: "",
  audience: "全体员工",
  status: "draft",
};

function formatDateTime(value: string | undefined): string {
  return value ? new Date(value).toLocaleString("zh-CN") : "-";
}

function noticeToInput(notice: Notice): NoticeInput {
  return {
    title: notice.title,
    content: notice.content,
    audience: notice.audience,
    status: notice.status,
  };
}

function badgeVariant(status: NoticeStatus): "secondary" | "success" | "warning" {
  if (status === "published") {
    return "success";
  }
  return status === "draft" ? "warning" : "secondary";
}

export function NoticesWorkspaceClient({ canManage, initialData }: NoticesWorkspaceClientProps): React.ReactElement {
  const [data, setData] = useState(initialData);
  const [query, setQuery] = useState("");
  const [statusFilter, setStatusFilter] = useState<"all" | NoticeStatus>("all");
  const [dialog, setDialog] = useState<DialogState | undefined>();
  const [message, setMessage] = useState("");
  const [isPending, setIsPending] = useState(false);
  const normalizedQuery = query.trim().toLowerCase();

  const filteredNotices = useMemo(
    () =>
      data.notices.filter((notice) => {
        const matchesQuery = !normalizedQuery || [notice.title, notice.content, notice.audience].join(" ").toLowerCase().includes(normalizedQuery);
        const matchesStatus = statusFilter === "all" || notice.status === statusFilter;
        return matchesQuery && matchesStatus;
      }),
    [data.notices, normalizedQuery, statusFilter],
  );

  async function refreshData(): Promise<void> {
    const response = await fetch("/api/notices");
    const result = (await response.json()) as ApiResult<{ data: NoticeCenterData }>;
    if (result.success) {
      setData(result.data);
    }
  }

  async function submitDialog(event: FormEvent<HTMLFormElement>): Promise<void> {
    event.preventDefault();
    if (!dialog || !canManage) {
      setMessage("当前角色无权维护公告");
      return;
    }

    setIsPending(true);
    const response = await fetch(dialog.mode === "create" ? "/api/notices" : `/api/notices/${dialog.id}`, {
      method: dialog.mode === "create" ? "POST" : "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(dialog.value),
    });
    const result = (await response.json()) as ApiResult<Record<string, unknown>>;
    setIsPending(false);
    setMessage(result.reason);
    if (result.success) {
      setDialog(undefined);
      await refreshData();
    }
  }

  async function remove(id: string): Promise<void> {
    if (!canManage) {
      setMessage("当前角色无权删除公告");
      return;
    }
    setIsPending(true);
    const response = await fetch(`/api/notices/${id}`, { method: "DELETE" });
    const result = (await response.json()) as ApiResult<Record<string, unknown>>;
    setIsPending(false);
    setMessage(result.reason);
    if (result.success) {
      await refreshData();
    }
  }

  async function markRead(id: string): Promise<void> {
    setIsPending(true);
    const response = await fetch(`/api/notices/${id}`, { method: "POST" });
    const result = (await response.json()) as ApiResult<Record<string, unknown>>;
    setIsPending(false);
    setMessage(result.reason);
    if (result.success) {
      await refreshData();
    }
  }

  return (
    <div className="mx-auto flex w-full max-w-7xl flex-col gap-5">
      <section className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
        <MetricCard icon={Bell} label="已发布" value={data.metrics.published} />
        <MetricCard icon={FileText} label="草稿" value={data.metrics.drafts} />
        <MetricCard icon={CheckCircle2} label="未读" value={data.metrics.unread} />
        <MetricCard icon={Undo2} label="已撤回" value={data.metrics.retracted} />
      </section>

      <section className="flex flex-col gap-3 border-b pb-4 xl:flex-row xl:items-center xl:justify-between">
        <div>
          <h1 className="text-3xl font-semibold tracking-normal">公告通知</h1>
          <p className="mt-2 text-sm text-muted-foreground">发布机构公告并跟踪阅读状态。</p>
        </div>
        <div className="grid gap-2 sm:grid-cols-2 xl:flex xl:items-center">
          <label className="relative block sm:col-span-2 xl:col-span-1">
            <Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
            <Input className="w-full pl-9 xl:w-72" onChange={(event) => setQuery(event.target.value)} placeholder="搜索公告或范围" value={query} />
          </label>
          <Select aria-label="公告状态" onValueChange={(value) => setStatusFilter(value as "all" | NoticeStatus)} options={[{ value: "all", label: "全部状态" }, ...NOTICE_STATUS_VALUES.map((value) => ({ value, label: NOTICE_STATUS_LABELS[value] }))]} value={statusFilter} />
          <Button disabled={!canManage} onClick={() => setDialog({ mode: "create", value: emptyNotice })} type="button">新增公告</Button>
        </div>
      </section>

      {message ? <p className="rounded-md bg-secondary px-3 py-2 text-sm text-secondary-foreground" role="status">{message}</p> : null}

      <Card>
        <CardHeader><CardTitle>公告列表</CardTitle></CardHeader>
        <CardContent className="overflow-x-auto">
          <Table className="w-full min-w-[980px] text-sm">
            <Table.Header className="bg-secondary text-left text-xs text-muted-foreground">
              <Table.Row>
                <Table.Head className="px-4 py-3">公告</Table.Head>
                <Table.Head className="px-4 py-3">范围</Table.Head>
                <Table.Head className="px-4 py-3">状态</Table.Head>
                <Table.Head className="px-4 py-3">阅读</Table.Head>
                <Table.Head className="px-4 py-3">发布时间</Table.Head>
                <Table.Head className="px-4 py-3 text-right">操作</Table.Head>
              </Table.Row>
            </Table.Header>
            <Table.Body className="divide-y bg-card">
              {filteredNotices.map((notice) => (
                <Table.Row key={notice.id}>
                  <Table.Cell className="px-4 py-3"><p className="font-medium">{notice.title}</p><p className="line-clamp-2 text-xs text-muted-foreground">{notice.content}</p></Table.Cell>
                  <Table.Cell className="px-4 py-3 text-muted-foreground">{notice.audience}</Table.Cell>
                  <Table.Cell className="px-4 py-3"><Badge variant={badgeVariant(notice.status)}>{NOTICE_STATUS_LABELS[notice.status]}</Badge></Table.Cell>
                  <Table.Cell className="px-4 py-3 text-muted-foreground">{notice.hasRead ? "已读" : "未读"} / {notice.readCount}</Table.Cell>
                  <Table.Cell className="px-4 py-3 text-muted-foreground">{formatDateTime(notice.publishedAt)}</Table.Cell>
                  <Table.Cell className="px-4 py-3"><div className="flex justify-end gap-2"><Button disabled={isPending || notice.hasRead} onClick={() => markRead(notice.id)} size="sm" type="button" variant="outline">已读</Button><Button disabled={!canManage || isPending} onClick={() => setDialog({ mode: "edit", id: notice.id, value: noticeToInput(notice) })} size="sm" type="button" variant="outline">编辑</Button><Button disabled={!canManage || isPending} onClick={() => remove(notice.id)} size="sm" type="button" variant="outline">删除</Button></div></Table.Cell>
                </Table.Row>
              ))}
              {filteredNotices.length === 0 ? <Table.Row><Table.Cell className="px-4 py-8 text-center text-muted-foreground" colSpan={6}>暂无公告</Table.Cell></Table.Row> : null}
            </Table.Body>
          </Table>
        </CardContent>
      </Card>

      <Dialog onClose={() => setDialog(undefined)} open={dialog !== undefined} title="公告通知" width="lg">
        {dialog ? (
          <form className="grid gap-3" onSubmit={submitDialog}>
            <Input label="标题" onChange={(event) => setDialog({ ...dialog, value: { ...dialog.value, title: event.target.value } })} required value={dialog.value.title} />
            <Input label="发布范围" onChange={(event) => setDialog({ ...dialog, value: { ...dialog.value, audience: event.target.value } })} value={dialog.value.audience} />
            <Select aria-label="公告状态" onValueChange={(status) => setDialog({ ...dialog, value: { ...dialog.value, status: status as NoticeStatus } })} options={NOTICE_STATUS_VALUES.map((status) => ({ value: status, label: NOTICE_STATUS_LABELS[status] }))} value={dialog.value.status} />
            <Textarea label="内容" onChange={(event) => setDialog({ ...dialog, value: { ...dialog.value, content: event.target.value } })} required value={dialog.value.content} />
            <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
              <Button disabled={isPending} onClick={() => setDialog(undefined)} type="button" variant="outline">取消</Button>
              <Button disabled={isPending || !canManage} type="submit">保存</Button>
            </div>
          </form>
        ) : null}
      </Dialog>
    </div>
  );
}

function MetricCard({ icon: Icon, label, value }: { icon: React.ComponentType<{ "aria-hidden"?: boolean; className?: string }>; label: string; value: number }): React.ReactElement {
  return (
    <Card>
      <CardHeader className="flex flex-row items-center justify-between p-4">
        <div><p className="text-sm text-muted-foreground">{label}</p><CardTitle className="mt-2 text-3xl">{value}</CardTitle></div>
        <Icon className="size-5 text-primary" aria-hidden={true} />
      </CardHeader>
    </Card>
  );
}
