import * as React from "react";
import { useTranslation } from "react-i18next";
import { CheckCircle2, Copy, FileText, Link2, Pencil, Plus, Puzzle, RefreshCw, Search, Trash2, Wrench, X } from "lucide-react";
import { createSkill, deleteSkill, listAllSkills, listToolConnections, listTools, updateSkill } from "@/api";
import { translateApiError } from "@/api/errors";
import { ApiErrorState } from "@/components/shared/ApiErrorState";
import { EmptyState } from "@/components/shared/EmptyState";
import { LoadingSpinner } from "@/components/shared/LoadingSpinner";
import { MetricCard } from "@/components/shared/MetricCard";
import { PageHeader } from "@/components/shared/PageHeader";
import { SearchInput } from "@/components/shared/SearchInput";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, 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 { toast } from "@/components/ui/toaster";
import type { SkillDefinition, ToolConnection, ToolDefinition } from "@/types";

type ToolOption = {
  id: string;
  name: string;
  description: string;
  toolType: string;
  exposedTools: Array<{
    name: string;
    description?: string;
  }>;
};

type SkillFormState = {
  name: string;
  description: string;
  instruction: string;
  category: string;
  selectedToolIds: string[];
};

const emptyForm: SkillFormState = {
  name: "",
  description: "",
  instruction: "",
  category: "service",
  selectedToolIds: [],
};

export function SkillsPage() {
  const { t } = useTranslation();
  const [skills, setSkills] = React.useState<SkillDefinition[]>([]);
  const [tools, setTools] = React.useState<ToolOption[]>([]);
  const [search, setSearch] = React.useState("");
  const [categoryFilter, setCategoryFilter] = React.useState("all");
  const [createOpen, setCreateOpen] = React.useState(false);
  const [editOpen, setEditOpen] = React.useState(false);
  const [editingSkill, setEditingSkill] = React.useState<SkillDefinition>();
  const [form, setForm] = React.useState<SkillFormState>(emptyForm);
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState<string>();

  const toolById = React.useMemo(() => new Map(tools.map((tool) => [tool.id, tool])), [tools]);
  const categories = Array.from(new Set(skills.map((skill) => skill.category || "service"))).sort();
  const filtered = skills
    .filter((skill) => skill.status !== "archived")
    .filter((skill) => {
      const toolNames = skill.toolIds.map((toolId) => toolOptionSearchText(toolById.get(toolId)) || toolId).join(" ");
      const text = `${skill.name} ${skill.description ?? ""} ${skill.category} ${skill.instruction} ${toolNames}`.toLowerCase();
      return text.includes(search.toLowerCase()) && (categoryFilter === "all" || skill.category === categoryFilter);
    })
    .sort((first, second) => first.name.localeCompare(second.name));
  const boundToolsCount = filtered.reduce((count, skill) => count + skill.toolIds.length, 0);

  const load = React.useCallback(async () => {
    setLoading(true);
    setError(undefined);
    try {
      const [skillItems, toolItems, connectionItems] = await Promise.all([
        listAllSkills(),
        listTools().catch(() => [] as ToolDefinition[]),
        listToolConnections().catch(() => [] as ToolConnection[]),
      ]);
      const connectionByTool = new Map<string, ToolConnection>();
      connectionItems.forEach((connection) => {
        if (!connectionByTool.has(connection.tool_id)) {
          connectionByTool.set(connection.tool_id, connection);
        }
      });
      const mappedTools = toolItems.map((tool) => ({
        id: tool.id,
        name: tool.name,
        description: tool.description ?? tool.tool_type,
        toolType: tool.tool_type,
        exposedTools: getMcpExposedTools(connectionByTool.get(tool.id)),
      }));
      setSkills(skillItems);
      setTools(mappedTools);
    } catch (err) {
      setError(translateApiError(err));
    } finally {
      setLoading(false);
    }
  }, [t]);

  React.useEffect(() => {
    void load();
  }, [load]);

  function openCreate() {
    setForm(emptyForm);
    setCreateOpen(true);
  }

  function openEdit(skill: SkillDefinition) {
    setEditingSkill(skill);
    setForm(fromSkill(skill));
    setEditOpen(true);
  }

  async function createSkillFromForm() {
    if (!form.name.trim()) return;
    setSaving(true);
    try {
      const created = await createSkill({
        name: form.name.trim(),
        description: form.description.trim() || null,
        category: form.category,
        instruction: form.instruction.trim(),
        toolIds: form.selectedToolIds,
      });
      setSkills((current) => [created, ...current]);
      setCreateOpen(false);
      toast.success(t("skills.created"));
    } catch (err) {
      toast.error(translateApiError(err));
    } finally {
      setSaving(false);
    }
  }

  async function updateSkillFromForm() {
    if (!editingSkill || !form.name.trim()) return;
    setSaving(true);
    try {
      const updated = await updateSkill({
        skillId: editingSkill.id,
        name: form.name.trim(),
        description: form.description.trim() || null,
        category: form.category,
        instruction: form.instruction.trim(),
        toolIds: form.selectedToolIds,
      });
      setSkills((current) => current.map((skill) => (skill.id === updated.id ? updated : skill)));
      setEditingSkill(updated);
      setEditOpen(false);
      toast.success(t("skills.saved"));
    } catch (err) {
      toast.error(translateApiError(err));
    } finally {
      setSaving(false);
    }
  }

  async function removeSkill(skill: SkillDefinition) {
    try {
      await deleteSkill(skill.id);
      setSkills((current) => current.filter((item) => item.id !== skill.id));
      toast.success(t("skills.deleted"));
    } catch (err) {
      toast.error(translateApiError(err));
    }
  }

  if (loading) return <LoadingSpinner label={t("agents.loadingSkills")} />;
  if (error) return <ApiErrorState message={error} onRetry={() => void load()} />;

  return (
    <div className="space-y-6">
      <PageHeader
        title={t("skills.title")}
        description={t("skills.description")}
        actions={
          <>
            <Button variant="outline" onClick={() => void load()}>
              <RefreshCw className="h-4 w-4" /> {t("common.refresh")}
            </Button>
            <Button onClick={openCreate}>
              <Plus className="h-4 w-4" /> {t("skills.new")}
            </Button>
          </>
        }
      />

      <div className="grid gap-4 md:grid-cols-3">
        <MetricCard label={t("skills.title")} value={filtered.length} icon={Puzzle} />
        <MetricCard label={t("skills.toolsCount")} value={boundToolsCount} icon={Wrench} />
        <MetricCard label={t("skills.category")} value={categories.length} icon={FileText} />
      </div>

      <Card>
        <CardHeader>
          <div className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
            <div>
              <CardTitle>{t("skills.title")}</CardTitle>
              <CardDescription>{filtered.length} / {skills.filter((skill) => skill.status !== "archived").length}</CardDescription>
            </div>
            <Badge className="w-fit border-border bg-muted/50 text-muted-foreground">{t("skills.selectTools")}</Badge>
          </div>
        </CardHeader>
        <CardContent className="space-y-4">
          <div className="grid gap-3 lg:grid-cols-[1fr_220px]">
            <SearchInput value={search} onChange={setSearch} placeholder={t("skills.search")} />
            <Select
              value={categoryFilter}
              onChange={(event) => setCategoryFilter(event.target.value)}
              options={[
                { value: "all", label: t("skills.allCategories") },
                ...categories.map((category) => ({ value: category, label: categoryLabel(category, t) })),
              ]}
            />
          </div>

          {filtered.length ? (
            <div className="overflow-hidden rounded-md border border-border">
              <div className="hidden grid-cols-[minmax(260px,1fr)_150px_minmax(260px,1fr)_110px] gap-4 border-b border-border bg-muted/35 px-4 py-3 text-xs font-medium uppercase tracking-wide text-muted-foreground lg:grid">
                <span>{t("common.name")}</span>
                <span>{t("skills.category")}</span>
                <span>{t("skills.toolsCount")}</span>
                <span className="text-right">{t("common.actions")}</span>
              </div>
              <div className="divide-y divide-border">
                {filtered.map((skill) => (
                  <SkillRow
                    key={skill.id}
                    skill={skill}
                    toolById={toolById}
                    onEdit={() => openEdit(skill)}
                    onDelete={() => void removeSkill(skill)}
                  />
                ))}
              </div>
            </div>
          ) : (
            <EmptyState
              icon={Search}
              title={t("skills.empty")}
              description={t("skills.emptyHint")}
              actionLabel={t("skills.new")}
              onAction={openCreate}
            />
          )}
        </CardContent>
      </Card>

      <SkillDialog
        open={createOpen}
        title={t("skills.new")}
        form={form}
        tools={tools}
        submitLabel={t("common.create")}
        saving={saving}
        onOpenChange={setCreateOpen}
        onChange={setForm}
        onSubmit={() => void createSkillFromForm()}
      />

      <SkillDialog
        open={editOpen}
        title={editingSkill?.name ?? t("common.edit")}
        form={form}
        tools={tools}
        submitLabel={t("common.save")}
        saving={saving}
        onOpenChange={setEditOpen}
        onChange={setForm}
        onSubmit={() => void updateSkillFromForm()}
      />
    </div>
  );
}

function SkillRow({
  skill,
  toolById,
  onEdit,
  onDelete,
}: {
  skill: SkillDefinition;
  toolById: Map<string, ToolOption>;
  onEdit: () => void;
  onDelete: () => void;
}) {
  const { t } = useTranslation();
  const boundTools = skill.toolIds.map((toolId) => ({
    id: toolId,
    label: toolDisplayName(toolById.get(toolId)) ?? toolId,
    missing: !toolById.has(toolId),
  }));

  return (
    <div className="grid gap-3 px-4 py-4 lg:grid-cols-[minmax(260px,1fr)_150px_minmax(260px,1fr)_110px] lg:items-center">
      <div className="min-w-0">
        <div className="flex items-center gap-2">
          <Puzzle className="h-4 w-4 text-primary" />
          <span className="truncate text-sm font-medium">{skill.name}</span>
        </div>
        <p className="mt-1 line-clamp-2 text-xs text-muted-foreground">{skill.description || t("skills.emptyHint")}</p>
      </div>
      <Badge className="w-fit border-border bg-muted/50 text-muted-foreground">{categoryLabel(skill.category, t)}</Badge>
      <div className="min-w-0">
        {boundTools.length ? (
          <div className="flex flex-wrap gap-1.5">
            {boundTools.slice(0, 3).map((tool) => (
              <Badge
                key={tool.id}
                className={tool.missing ? "border-destructive/30 bg-destructive/10 text-destructive" : "border-primary/20 bg-primary/10 text-primary"}
              >
                <Link2 className="h-3.5 w-3.5" /> <span className="max-w-32 truncate">{tool.label}</span>
              </Badge>
            ))}
            {boundTools.length > 3 ? (
              <Badge className="border-border bg-muted text-muted-foreground">+{boundTools.length - 3}</Badge>
            ) : null}
          </div>
        ) : (
          <span className="text-xs text-muted-foreground">{t("skills.noToolsSelected", "No tools selected")}</span>
        )}
      </div>
      <div className="flex items-center justify-start gap-1.5 lg:justify-end">
        <Button size="icon" variant="ghost" onClick={onEdit} aria-label={`Edit ${skill.name}`}>
          <Pencil className="h-4 w-4" />
        </Button>
        <Button size="icon" variant="ghost" className="text-destructive hover:text-destructive" onClick={onDelete} aria-label={`Delete ${skill.name}`}>
          <Trash2 className="h-4 w-4" />
        </Button>
      </div>
    </div>
  );
}

function SkillDialog({
  open,
  title,
  form,
  tools,
  submitLabel,
  saving,
  onOpenChange,
  onChange,
  onSubmit,
}: {
  open: boolean;
  title: string;
  form: SkillFormState;
  tools: ToolOption[];
  submitLabel: string;
  saving: boolean;
  onOpenChange: (open: boolean) => void;
  onChange: (form: SkillFormState) => void;
  onSubmit: () => void;
}) {
  const { t } = useTranslation();
  const [toolSearch, setToolSearch] = React.useState("");
  const set = (key: keyof SkillFormState, value: string | string[]) => onChange({ ...form, [key]: value });
  const selectedTools = form.selectedToolIds
    .map((toolId) => tools.find((tool) => tool.id === toolId))
    .filter((tool): tool is ToolOption => Boolean(tool));
  const filteredTools = tools
    .filter((tool) => {
      const text = toolOptionSearchText(tool).toLowerCase();
      return text.includes(toolSearch.toLowerCase().trim());
    })
    .sort((first, second) => {
      const firstSelected = form.selectedToolIds.includes(first.id) ? 0 : 1;
      const secondSelected = form.selectedToolIds.includes(second.id) ? 0 : 1;
      return firstSelected - secondSelected || first.name.localeCompare(second.name);
    });

  React.useEffect(() => {
    if (open) setToolSearch("");
  }, [open]);

  function toggleTool(toolId: string) {
    const selectedToolIds = form.selectedToolIds.includes(toolId)
      ? form.selectedToolIds.filter((id) => id !== toolId)
      : [...form.selectedToolIds, toolId];
    set("selectedToolIds", selectedToolIds);
  }

  function selectVisibleTools() {
    const next = new Set(form.selectedToolIds);
    filteredTools.forEach((tool) => next.add(tool.id));
    set("selectedToolIds", Array.from(next));
  }

  async function copyToolName(name: string) {
    try {
      await navigator.clipboard.writeText(name);
      toast.success(t("common.copied", "Copied"));
    } catch {
      toast.error(t("errors.failedToCopy", "Failed to copy"));
    }
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange} title={title} className="max-w-6xl">
      <div className="grid gap-5 xl:grid-cols-[minmax(0,1fr)_430px]">
        <div className="space-y-4">
          <section className="rounded-md border border-border bg-surface-elevated p-4">
            <div className="mb-4 flex items-center justify-between gap-3">
              <div>
                <h3 className="text-sm font-semibold">{t("skills.skillSetup", "Skill setup")}</h3>
                <p className="mt-1 text-xs text-muted-foreground">{t("skills.skillSetupHint", "Name the skill and write the instruction it should follow.")}</p>
              </div>
              <Badge className={form.name.trim() ? "border-green-500/30 bg-green-500/10 text-green-700 dark:text-green-200" : "border-border bg-muted text-muted-foreground"}>
                {form.name.trim() ? t("common.ready", "Ready") : t("common.required", "Required")}
              </Badge>
            </div>

            <div className="grid gap-4 md:grid-cols-[minmax(0,1fr)_190px]">
              <Field label={t("common.name")}>
                <Input value={form.name} onChange={(event) => set("name", event.target.value)} placeholder={t("skills.namePlaceholder")} />
              </Field>
              <Field label={t("skills.category")}>
                <Select
                  value={form.category}
                  onChange={(event) => set("category", event.target.value)}
                  options={[
                    { value: "service", label: t("skills.catService") },
                    { value: "analytics", label: t("skills.catAnalytics") },
                    { value: "development", label: t("skills.catDevelopment") },
                    { value: "processing", label: t("skills.catProcessing") },
                  ]}
                />
              </Field>
              <div className="md:col-span-2">
                <Field label={t("common.description")}>
                  <Input value={form.description} onChange={(event) => set("description", event.target.value)} placeholder={t("skills.descPlaceholder")} />
                </Field>
              </div>
            </div>
          </section>

          <section className="rounded-md border border-border bg-surface-elevated p-4">
            <div className="mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
              <div>
                <h3 className="text-sm font-semibold">{t("skills.instruction")}</h3>
                <p className="mt-1 text-xs text-muted-foreground">{t("skills.instructionBuilderHint", "Write when to use the skill, what tools to call, and what output to return.")}</p>
              </div>
              <Badge className="w-fit border-border bg-muted text-muted-foreground">
                {form.instruction.trim().length} {t("common.characters", "characters")}
              </Badge>
            </div>
            <Textarea
              value={form.instruction}
              onChange={(event) => set("instruction", event.target.value)}
              placeholder={t("skills.instructionPlaceholder")}
              className="min-h-72 font-mono leading-6"
            />
          </section>
        </div>

        <aside className="space-y-4">
          <section className="rounded-md border border-border bg-surface-elevated">
            <div className="border-b border-border p-4">
              <div className="flex items-start justify-between gap-3">
                <div>
                  <h3 className="text-sm font-semibold">{t("skills.selectTools")}</h3>
                  <p className="mt-1 text-xs text-muted-foreground">{t("skills.toolsHint")}</p>
                </div>
                <Badge className="shrink-0 border-primary/20 bg-primary/10 text-primary">
                  {form.selectedToolIds.length}
                </Badge>
              </div>

              <div className="mt-3 grid gap-2">
                <SearchInput
                  value={toolSearch}
                  onChange={setToolSearch}
                  placeholder={t("skills.searchTools", "Search tools")}
                />
                <div className="grid gap-2 sm:grid-cols-2">
                  <Button type="button" variant="outline" onClick={selectVisibleTools} disabled={!filteredTools.length}>
                    <CheckCircle2 className="h-4 w-4" /> {t("common.selectAll", "Select all")}
                  </Button>
                  <Button type="button" variant="ghost" onClick={() => set("selectedToolIds", [])} disabled={!form.selectedToolIds.length}>
                    <X className="h-4 w-4" /> {t("common.clear", "Clear")}
                  </Button>
                </div>
              </div>

              <div className="mt-3 min-h-10 rounded-md border border-dashed border-border bg-background/60 p-2">
                {selectedTools.length ? (
                  <div className="flex flex-wrap gap-1.5">
                    {selectedTools.map((tool) => (
                      <button
                        key={tool.id}
                        type="button"
                        onClick={() => toggleTool(tool.id)}
                        className="inline-flex max-w-full items-center gap-1.5 rounded-md border border-primary/20 bg-primary/10 px-2 py-1 text-xs font-medium text-primary transition hover:bg-primary/15"
                      >
                        <span className="truncate">{toolDisplayName(tool)}</span>
                        <X className="h-3 w-3 shrink-0" />
                      </button>
                    ))}
                  </div>
                ) : (
                  <p className="px-1 py-1 text-xs text-muted-foreground">
                    {t("skills.noToolsSelected", "No tools selected")}
                  </p>
                )}
              </div>
            </div>

            <div className="max-h-[46dvh] overflow-auto">
              {filteredTools.length ? (
                <div className="divide-y divide-border">
                  {filteredTools.map((tool) => {
                    const selected = form.selectedToolIds.includes(tool.id);
                    const displayName = toolDisplayName(tool) ?? tool.name;
                    return (
                      <div
                        key={tool.id}
                        className={[
                          "grid grid-cols-[24px_minmax(0,1fr)] gap-3 px-3 py-3 transition hover:bg-muted/35",
                          selected ? "bg-primary/5" : "bg-transparent",
                        ].join(" ")}
                      >
                        <button
                          type="button"
                          onClick={() => toggleTool(tool.id)}
                          className={[
                            "mt-0.5 grid h-5 w-5 place-items-center rounded border",
                            selected ? "border-primary bg-primary text-primary-foreground" : "border-muted-foreground/40",
                          ].join(" ")}
                          aria-label={selected ? t("common.selected", "Selected") : t("skills.selectTools")}
                        >
                          {selected ? <CheckCircle2 className="h-3.5 w-3.5" /> : null}
                        </button>
                        <div className="min-w-0">
                          <button type="button" className="block w-full text-left" onClick={() => toggleTool(tool.id)}>
                            <span className="flex min-w-0 items-center gap-2">
                              <span className="truncate text-sm font-medium">{displayName}</span>
                              <Badge className="shrink-0 border-border bg-muted text-muted-foreground">
                                {tool.toolType === "mcp" ? t("tools.mcpServer", "MCP server") : tool.toolType}
                              </Badge>
                            </span>
                            <span className="mt-1 block line-clamp-2 text-xs text-muted-foreground">
                              {tool.exposedTools.length
                                ? `${tool.name} / ${tool.exposedTools.length} ${t("tools.exposedTools", "exposed tools")}`
                                : tool.description}
                            </span>
                          </button>

                          <div className="mt-2 flex flex-wrap gap-1.5">
                            {(tool.exposedTools.length ? tool.exposedTools.slice(0, 4) : [{ name: displayName }]).map((exposedTool) => (
                              <span
                                key={exposedTool.name}
                                className="inline-flex items-center gap-1 rounded border border-border bg-background px-1.5 py-0.5 font-mono text-xs text-muted-foreground"
                              >
                                {exposedTool.name}
                                <button
                                  type="button"
                                  className="grid h-5 w-5 place-items-center rounded text-muted-foreground transition hover:bg-muted hover:text-foreground"
                                  aria-label={t("skills.copyToolName", "Copy tool name")}
                                  onClick={() => void copyToolName(exposedTool.name)}
                                >
                                  <Copy className="h-3 w-3" />
                                </button>
                              </span>
                            ))}
                            {tool.exposedTools.length > 4 ? (
                              <span className="text-xs text-muted-foreground">+{tool.exposedTools.length - 4}</span>
                            ) : null}
                          </div>
                        </div>
                      </div>
                    );
                  })}
                </div>
              ) : (
                <div className="p-4 text-sm text-muted-foreground">
                  {tools.length ? t("skills.noToolsMatch", "No tools match this search") : t("skills.noTools")}
                </div>
              )}
            </div>
          </section>
        </aside>
      </div>

      <div className="sticky bottom-0 -mx-4 -mb-4 mt-5 flex flex-col-reverse gap-3 border-t border-border bg-surface-elevated p-4 sm:-mx-5 sm:-mb-5 sm:flex-row sm:items-center sm:justify-between">
        <div className="text-xs text-muted-foreground">
          {form.name.trim() || t("skills.namePlaceholder")} / {form.selectedToolIds.length} {t("skills.toolsCount")}
        </div>
        <div className="flex flex-col-reverse gap-2 sm:flex-row">
          <Button type="button" variant="ghost" onClick={() => onOpenChange(false)}>{t("common.cancel")}</Button>
          <Button type="button" onClick={onSubmit} disabled={!form.name.trim() || saving}>{saving ? t("common.saving") : submitLabel}</Button>
        </div>
      </div>
    </Dialog>
  );
}

function Field({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <label className="block space-y-2 text-sm font-medium">
      <span>{label}</span>
      {children}
    </label>
  );
}

function fromSkill(skill: SkillDefinition): SkillFormState {
  return {
    name: skill.name,
    description: skill.description ?? "",
    instruction: skill.instruction,
    category: skill.category || "service",
    selectedToolIds: skill.toolIds,
  };
}

function toolDisplayName(tool?: ToolOption) {
  if (!tool) return undefined;
  const onlyExposedTool = tool.exposedTools[0];
  if (tool.exposedTools.length === 1 && onlyExposedTool) return onlyExposedTool.name;
  return tool.name;
}

function toolOptionSearchText(tool?: ToolOption) {
  if (!tool) return "";
  const exposedText = tool.exposedTools
    .map((exposedTool) => `${exposedTool.name} ${exposedTool.description ?? ""}`)
    .join(" ");
  return `${tool.name} ${tool.description} ${tool.toolType} ${exposedText}`;
}

function getMcpExposedTools(connection?: ToolConnection) {
  const config = connection?.invoke_config_json;
  if (!config || typeof config !== "object") return [];
  const rawTools = config.mcp_tools ?? config.tools ?? config.tool_names;
  if (!Array.isArray(rawTools)) return [];
  return rawTools.flatMap((item) => {
    if (!item || typeof item !== "object" || Array.isArray(item)) return [];
    const record = item as Record<string, unknown>;
    if (typeof record.name !== "string" || !record.name) return [];
    return [{
      name: record.name,
      description: typeof record.description === "string" ? record.description : undefined,
    }];
  });
}

function categoryLabel(category: string, t: (key: string) => string) {
  const key = `skills.cat${formatCategoryKey(category)}`;
  const label = t(key);
  return label === key ? category : label;
}

function formatCategoryKey(category: string) {
  return category
    .split(/[_-]+/)
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join("");
}
