import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
import { Form, useFetcher, Link } from "@remix-run/react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { Button, LinkButton } from "~/components/primitives/Buttons";
import { Input } from "~/components/primitives/Input";
import { PaginationControls } from "~/components/primitives/Pagination";
import { Paragraph } from "~/components/primitives/Paragraph";
import {
  Table,
  TableBlankRow,
  TableBody,
  TableCell,
  TableHeader,
  TableHeaderCell,
  TableRow,
} from "~/components/primitives/Table";
import { prisma } from "~/db.server";
import { dashboardAction, dashboardLoader } from "~/services/routeBuilders/dashboardBuilder";
import { createSearchParams } from "~/utils/searchParams";
import { seedLlmPricing, syncLlmCatalog } from "@internal/llm-model-catalog";
import { llmPricingRegistry } from "~/v3/llmPricingRegistry.server";

const PAGE_SIZE = 50;

const SearchParams = z.object({
  page: z.coerce.number().optional(),
  search: z.string().optional(),
});

export const loader = dashboardLoader(
  { authorization: { requireSuper: true } },
  async ({ request }) => {
    const searchParams = createSearchParams(request.url, SearchParams);
    if (!searchParams.success) throw new Error(searchParams.error);
    const { page: rawPage, search } = searchParams.params.getAll();
    const page = rawPage ?? 1;

    const where = {
      projectId: null as string | null,
      ...(search ? { modelName: { contains: search, mode: "insensitive" as const } } : {}),
    };

    const [rawModels, total] = await Promise.all([
      prisma.llmModel.findMany({
        where,
        include: {
          pricingTiers: { include: { prices: true }, orderBy: { priority: "asc" } },
        },
        orderBy: { modelName: "asc" },
        skip: (page - 1) * PAGE_SIZE,
        take: PAGE_SIZE,
      }),
      prisma.llmModel.count({ where }),
    ]);

    // Convert Prisma Decimal to plain numbers for serialization
    const models = rawModels.map((m) => ({
      ...m,
      pricingTiers: m.pricingTiers.map((t) => ({
        ...t,
        prices: t.prices.map((p) => ({ ...p, price: Number(p.price) })),
      })),
    }));

    return typedjson({
      models,
      total,
      page,
      pageCount: Math.ceil(total / PAGE_SIZE),
      filters: { search },
    });
  }
);

export const action = dashboardAction(
  { authorization: { requireSuper: true } },
  async ({ request }) => {
    const formData = await request.formData();
    const _action = formData.get("_action");

    if (_action === "seed") {
      console.log("[admin] seed action started");
      const result = await seedLlmPricing(prisma);
      console.log(
        `[admin] seed complete: ${result.modelsCreated} created, ${result.modelsSkipped} skipped, ${result.modelsUpdated} updated`
      );
      await llmPricingRegistry?.reload();
      console.log("[admin] registry reloaded after seed");
      return typedjson({
        success: true,
        message: `Seeded: ${result.modelsCreated} created, ${result.modelsSkipped} skipped, ${result.modelsUpdated} updated`,
      });
    }

    if (_action === "sync") {
      console.log("[admin] sync catalog action started");
      const result = await syncLlmCatalog(prisma);
      console.log(
        `[admin] sync complete: ${result.modelsUpdated} updated, ${result.modelsSkipped} skipped`
      );
      await llmPricingRegistry?.reload();
      console.log("[admin] registry reloaded after sync");
      return typedjson({
        success: true,
        message: `Synced: ${result.modelsUpdated} updated, ${result.modelsSkipped} skipped`,
      });
    }

    if (_action === "reload") {
      console.log("[admin] reload action started");
      await llmPricingRegistry?.reload();
      console.log("[admin] registry reloaded");
      return typedjson({ success: true, message: "Registry reloaded" });
    }

    if (_action === "test") {
      const modelString = formData.get("modelString");
      if (typeof modelString !== "string" || !modelString) {
        return typedjson({ testResult: null });
      }

      // Use the registry's match() which handles prefix stripping automatically
      const matched = llmPricingRegistry?.match(modelString) ?? null;

      return typedjson({
        testResult: {
          modelString,
          match: matched ? { friendlyId: matched.friendlyId, modelName: matched.modelName } : null,
        },
      });
    }

    if (_action === "delete") {
      const modelId = formData.get("modelId");
      if (typeof modelId === "string") {
        await prisma.llmModel.delete({ where: { id: modelId } });
        await llmPricingRegistry?.reload();
      }
      return typedjson({ success: true });
    }

    return typedjson({ error: "Unknown action" }, { status: 400 });
  }
);

export default function AdminLlmModelsRoute() {
  const { models, filters, page, pageCount, total } = useTypedLoaderData<typeof loader>();
  const seedFetcher = useFetcher();
  const syncFetcher = useFetcher();
  const reloadFetcher = useFetcher();
  const testFetcher = useFetcher<{
    testResult?: {
      modelString: string;
      match: { friendlyId: string; modelName: string } | null;
    } | null;
  }>();

  const testResult = testFetcher.data?.testResult;

  return (
    <main className="flex h-full min-w-0 flex-1 flex-col overflow-y-auto px-4 pb-4">
      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <Form className="flex items-center gap-2">
            <Input
              placeholder="Search models..."
              variant="medium"
              icon={MagnifyingGlassIcon}
              fullWidth={true}
              name="search"
              defaultValue={filters.search}
              autoFocus
            />
            <Button type="submit" variant="secondary/medium">
              Search
            </Button>
          </Form>

          <div className="flex items-center gap-2">
            <seedFetcher.Form method="post">
              <input type="hidden" name="_action" value="seed" />
              <Button
                type="submit"
                variant="tertiary/small"
                disabled={seedFetcher.state !== "idle"}
              >
                {seedFetcher.state !== "idle" ? "Seeding..." : "Seed defaults"}
              </Button>
            </seedFetcher.Form>

            <syncFetcher.Form method="post">
              <input type="hidden" name="_action" value="sync" />
              <Button
                type="submit"
                variant="tertiary/small"
                disabled={syncFetcher.state !== "idle"}
              >
                {syncFetcher.state !== "idle" ? "Syncing..." : "Sync catalog"}
              </Button>
            </syncFetcher.Form>

            <reloadFetcher.Form method="post">
              <input type="hidden" name="_action" value="reload" />
              <Button
                type="submit"
                variant="tertiary/small"
                disabled={reloadFetcher.state !== "idle"}
              >
                {reloadFetcher.state !== "idle" ? "Reloading..." : "Reload registry"}
              </Button>
            </reloadFetcher.Form>

            <LinkButton to="/admin/llm-models/missing" variant="tertiary/small">
              Missing models
            </LinkButton>

            <LinkButton to="/admin/llm-models/new" variant="primary/small">
              Add model
            </LinkButton>
          </div>
        </div>

        {/* Model tester */}
        <div className="rounded-md border border-grid-dimmed bg-background-bright p-3 space-y-2">
          <label className="text-xs font-medium text-text-dimmed">
            Test model string — paste a model name to see which pricing model matches
          </label>
          <testFetcher.Form method="post" className="flex items-center gap-2">
            <input type="hidden" name="_action" value="test" />
            <Input
              name="modelString"
              placeholder="e.g. anthropic/claude-haiku-4-5-20251001, gpt-4o-mini, mistral/mistral-large-3"
              variant="medium"
              fullWidth
              className="font-mono text-xs"
            />
            <Button type="submit" variant="secondary/small" disabled={testFetcher.state !== "idle"}>
              Test
            </Button>
          </testFetcher.Form>
          {testResult !== undefined && testResult !== null && (
            <div className="text-xs space-y-1">
              <span className="text-text-dimmed">
                Testing:{" "}
                <span className="font-mono text-text-bright">{testResult.modelString}</span>
              </span>
              {testResult.match ? (
                <div className="text-green-400">
                  Match:{" "}
                  <Link
                    to={`/admin/llm-models/${testResult.match.friendlyId}`}
                    className="underline font-medium"
                  >
                    {testResult.match.modelName}
                  </Link>
                </div>
              ) : (
                <div className="text-red-400">No match found — this model has no pricing data</div>
              )}
            </div>
          )}
        </div>

        <div className="flex items-center justify-between">
          <Paragraph className="text-text-dimmed">
            {total} global models (page {page} of {pageCount})
          </Paragraph>
          <PaginationControls currentPage={page} totalPages={pageCount} />
        </div>

        <Table>
          <TableHeader>
            <TableRow>
              <TableHeaderCell>Model Name</TableHeaderCell>
              <TableHeaderCell>Source</TableHeaderCell>
              <TableHeaderCell>Input $/tok</TableHeaderCell>
              <TableHeaderCell>Output $/tok</TableHeaderCell>
              <TableHeaderCell>Other prices</TableHeaderCell>
            </TableRow>
          </TableHeader>
          <TableBody>
            {models.length === 0 ? (
              <TableBlankRow colSpan={5}>
                <Paragraph>No models found</Paragraph>
              </TableBlankRow>
            ) : (
              models.map((model) => {
                // Get default tier prices
                const defaultTier =
                  model.pricingTiers.find((t) => t.isDefault) ?? model.pricingTiers[0];
                const priceMap = defaultTier
                  ? Object.fromEntries(defaultTier.prices.map((p) => [p.usageType, p.price]))
                  : {};
                const inputPrice = priceMap["input"];
                const outputPrice = priceMap["output"];
                const otherPrices = defaultTier
                  ? defaultTier.prices.filter(
                      (p) => p.usageType !== "input" && p.usageType !== "output"
                    )
                  : [];

                return (
                  <TableRow key={model.id}>
                    <TableCell>
                      <Link
                        to={`/admin/llm-models/${model.friendlyId}`}
                        className="font-medium text-indigo-500 underline"
                      >
                        {model.modelName}
                      </Link>
                    </TableCell>
                    <TableCell>
                      <span
                        className={`inline-flex rounded-sm px-1.5 py-0.5 text-[11px] font-medium ${
                          model.source === "admin"
                            ? "bg-amber-500/20 text-amber-400"
                            : "bg-background-raised text-text-dimmed"
                        }`}
                      >
                        {model.source ?? "default"}
                      </span>
                    </TableCell>
                    <TableCell>
                      <span className="text-xs font-mono text-text-bright">
                        {inputPrice != null ? formatPrice(inputPrice) : "-"}
                      </span>
                    </TableCell>
                    <TableCell>
                      <span className="text-xs font-mono text-text-bright">
                        {outputPrice != null ? formatPrice(outputPrice) : "-"}
                      </span>
                    </TableCell>
                    <TableCell>
                      {otherPrices.length > 0 ? (
                        <span
                          className="text-xs text-text-dimmed"
                          title={otherPrices.map((p) => p.usageType).join(", ")}
                        >
                          +{otherPrices.length} more
                        </span>
                      ) : (
                        <span className="text-xs text-text-dimmed">-</span>
                      )}
                    </TableCell>
                  </TableRow>
                );
              })
            )}
          </TableBody>
        </Table>

        <PaginationControls currentPage={page} totalPages={pageCount} />
      </div>
    </main>
  );
}

/** Format a per-token price as $/M tokens for readability */
function formatPrice(perToken: number): string {
  const perMillion = perToken * 1_000_000;
  if (perMillion >= 1) return `$${perMillion.toFixed(2)}/M`;
  if (perMillion >= 0.01) return `$${perMillion.toFixed(4)}/M`;
  return `$${perMillion.toFixed(6)}/M`;
}
