import { ArrowsRightLeftIcon } from "@heroicons/react/20/solid";
import { type MetaFunction } from "@remix-run/react";
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { useState } from "react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { InlineCode } from "~/components/code/InlineCode";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import type { QueryWidgetConfig } from "~/components/metrics/QueryWidget";
import { Badge } from "~/components/primitives/Badge";
import { LinkButton } from "~/components/primitives/Buttons";
import { Callout } from "~/components/primitives/Callout";
import { Header2 } from "~/components/primitives/Headers";
import { Input } from "~/components/primitives/Input";
import { Label } from "~/components/primitives/Label";
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
import * as Property from "~/components/primitives/PropertyTable";
import { TabButton, TabContainer } from "~/components/primitives/Tabs";
import { useEnvironment } from "~/hooks/useEnvironment";
import { useOrganization } from "~/hooks/useOrganizations";
import { useProject } from "~/hooks/useProject";
import { findProjectBySlug } from "~/models/project.server";
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
import { ModelRegistryPresenter } from "~/presenters/v3/ModelRegistryPresenter.server";
import { MetricWidget } from "~/routes/resources.metric";
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
import { requireUserId } from "~/services/session.server";
import {
  formatFeature,
  formatModelCost,
  formatModelPrice,
  formatProviderName,
  formatTokenCount,
} from "~/utils/modelFormatters";
import { EnvironmentParamSchema, v3ModelComparePath, v3ModelsPath } from "~/utils/pathBuilder";

const ParamSchema = EnvironmentParamSchema.extend({
  modelId: z.string(),
});

export const meta: MetaFunction = () => {
  return [{ title: "Model Detail | Trigger.dev" }];
};

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
  const userId = await requireUserId(request);
  const { organizationSlug, projectParam, envParam, modelId } = ParamSchema.parse(params);

  const project = await findProjectBySlug(organizationSlug, projectParam, userId);
  if (!project) {
    throw new Response("Project not found", { status: 404 });
  }

  const environment = await findEnvironmentBySlug(project.id, envParam, userId);
  if (!environment) {
    throw new Response("Environment not found", { status: 404 });
  }

  const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
    project.organizationId,
    "standard"
  );
  const presenter = new ModelRegistryPresenter(clickhouse);
  const model = await presenter.getModelDetail(modelId);

  if (!model) {
    throw new Response("Model not found", { status: 404 });
  }

  const now = new Date();
  const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);

  const userMetrics = await presenter.getUserMetrics(
    model.modelName,
    project.id,
    environment.id,
    sevenDaysAgo,
    now
  );

  return typedjson({
    model,
    userMetrics,
    organizationId: project.organizationId,
    projectId: project.id,
    environmentId: environment.id,
  });
};

/** Escape a value for safe interpolation into a TSQL single-quoted string. */
function escapeTSQL(value: string): string {
  return value.replace(/'/g, "''");
}

function bignumberConfig(
  column: string,
  opts?: { aggregation?: "sum" | "avg" | "first"; suffix?: string; abbreviate?: boolean }
): QueryWidgetConfig {
  return {
    type: "bignumber",
    column,
    aggregation: opts?.aggregation ?? "sum",
    abbreviate: opts?.abbreviate ?? false,
    suffix: opts?.suffix,
  };
}

function chartConfig(opts: {
  chartType: "bar" | "line";
  xAxisColumn: string;
  yAxisColumns: string[];
  aggregation?: "sum" | "avg";
}): QueryWidgetConfig {
  return {
    type: "chart",
    chartType: opts.chartType,
    xAxisColumn: opts.xAxisColumn,
    yAxisColumns: opts.yAxisColumns,
    groupByColumn: null,
    stacked: false,
    sortByColumn: null,
    sortDirection: "asc",
    aggregation: opts.aggregation ?? "sum",
  };
}

type Tab = "overview" | "global" | "usage";

const TAB_CONFIG: { id: Tab; label: string }[] = [
  { id: "overview", label: "Overview" },
  { id: "usage", label: "Metrics" },
  { id: "global", label: "Global metrics" },
];

export default function ModelDetailPage() {
  const { model, userMetrics, organizationId, projectId, environmentId } =
    useTypedLoaderData<typeof loader>();
  const organization = useOrganization();
  const project = useProject();
  const environment = useEnvironment();
  const [activeTab, setActiveTab] = useState<Tab>("overview");

  return (
    <PageContainer>
      <NavBar>
        <PageTitle
          title={model.displayId}
          backButton={{
            to: v3ModelsPath(organization, project, environment),
            text: "Models",
          }}
        />
        <PageAccessories>
          <LinkButton
            variant="tertiary/small"
            to={`${v3ModelComparePath(organization, project, environment)}?models=${model.modelName}`}
            LeadingIcon={ArrowsRightLeftIcon}
          >
            Compare with...
          </LinkButton>
        </PageAccessories>
      </NavBar>
      <PageBody scrollable>
        <TabContainer>
          {TAB_CONFIG.map((tab) => (
            <TabButton
              key={tab.id}
              isActive={activeTab === tab.id}
              layoutId="model-detail-tabs"
              onClick={() => setActiveTab(tab.id)}
            >
              {tab.label}
            </TabButton>
          ))}
        </TabContainer>

        <div className="p-4">
          {activeTab === "overview" && <OverviewTab model={model} userMetrics={userMetrics} />}
          {activeTab === "global" && (
            <GlobalMetricsTab
              modelName={model.modelName}
              organizationId={organizationId}
              projectId={projectId}
              environmentId={environmentId}
            />
          )}
          {activeTab === "usage" && (
            <YourUsageTab
              modelName={model.modelName}
              organizationId={organizationId}
              projectId={projectId}
              environmentId={environmentId}
            />
          )}
        </div>
      </PageBody>
    </PageContainer>
  );
}

// --- Cost Estimator ---

function CostEstimator({
  inputPrice,
  outputPrice,
  defaultInputTokens,
  defaultOutputTokens,
}: {
  inputPrice: number | null;
  outputPrice: number | null;
  defaultInputTokens?: number;
  defaultOutputTokens?: number;
}) {
  const [inputTokens, setInputTokens] = useState(defaultInputTokens ?? 1000);
  const [outputTokens, setOutputTokens] = useState(defaultOutputTokens ?? 500);
  const [numCalls, setNumCalls] = useState(1000);

  if (inputPrice === null && outputPrice === null) return null;

  const inputCost = inputTokens * (inputPrice ?? 0) * numCalls;
  const outputCost = outputTokens * (outputPrice ?? 0) * numCalls;
  const totalCost = inputCost + outputCost;

  return (
    <div className="rounded-md border border-grid-dimmed p-4">
      <Header2 className="mb-3">Cost Estimator</Header2>
      <div className="space-y-3">
        <div className="grid grid-cols-3 gap-3">
          <div className="space-y-1.5">
            <Label htmlFor="input-tokens">Input tokens/call</Label>
            <Input
              id="input-tokens"
              variant="medium"
              fullWidth
              type="number"
              value={inputTokens}
              onChange={(e) => setInputTokens(parseInt(e.target.value) || 0)}
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="output-tokens">Output tokens/call</Label>
            <Input
              id="output-tokens"
              variant="medium"
              fullWidth
              type="number"
              value={outputTokens}
              onChange={(e) => setOutputTokens(parseInt(e.target.value) || 0)}
            />
          </div>
          <div className="space-y-1.5">
            <Label htmlFor="num-calls">Number of calls</Label>
            <Input
              id="num-calls"
              variant="medium"
              fullWidth
              type="number"
              value={numCalls}
              onChange={(e) => setNumCalls(parseInt(e.target.value) || 0)}
            />
          </div>
        </div>
        <Callout variant="info">
          <div className="text-lg font-semibold tabular-nums text-text-bright">
            {formatModelCost(totalCost)}
          </div>
          <div className="mt-1 space-y-0.5 text-xs tabular-nums text-text-dimmed">
            <div>
              Input: {formatModelCost(inputCost)} ({formatTokenCount(inputTokens * numCalls)} tokens
              x {formatModelPrice(inputPrice)}/1M)
            </div>
            <div>
              Output: {formatModelCost(outputCost)} ({formatTokenCount(outputTokens * numCalls)}{" "}
              tokens x {formatModelPrice(outputPrice)}/1M)
            </div>
          </div>
        </Callout>
      </div>
    </div>
  );
}

// --- Overview Tab ---

function OverviewTab({
  model,
  userMetrics,
}: {
  model: ReturnType<typeof useTypedLoaderData<typeof loader>>["model"];
  userMetrics: ReturnType<typeof useTypedLoaderData<typeof loader>>["userMetrics"];
}) {
  return (
    <div className="space-y-4">
      <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
        {/* Model Info */}
        <div className="rounded-md border border-grid-dimmed p-4">
          <Header2 className="mb-3">Model Info</Header2>
          <Property.Table>
            <Property.Item>
              <Property.Label>Provider</Property.Label>
              <Property.Value>{formatProviderName(model.provider)}</Property.Value>
            </Property.Item>
            <Property.Item>
              <Property.Label>Model Name</Property.Label>
              <Property.Value>
                <InlineCode variant="small">{model.modelName}</InlineCode>
              </Property.Value>
            </Property.Item>
            {model.description && (
              <Property.Item>
                <Property.Label>Description</Property.Label>
                <Property.Value>{model.description}</Property.Value>
              </Property.Item>
            )}
            {model.contextWindow && (
              <Property.Item>
                <Property.Label>Context Window</Property.Label>
                <Property.Value>{formatTokenCount(model.contextWindow)} tokens</Property.Value>
              </Property.Item>
            )}
            {model.maxOutputTokens && (
              <Property.Item>
                <Property.Label>Max Output</Property.Label>
                <Property.Value>{formatTokenCount(model.maxOutputTokens)} tokens</Property.Value>
              </Property.Item>
            )}
            {model.features.length > 0 && (
              <Property.Item>
                <Property.Label>Features</Property.Label>
                <Property.Value>
                  <div className="flex flex-wrap gap-1">
                    {model.features.map((f) => (
                      <Badge key={f} variant="outline-rounded">
                        {formatFeature(f)}
                      </Badge>
                    ))}
                  </div>
                </Property.Value>
              </Property.Item>
            )}
            <Property.Item>
              <Property.Label>Match Pattern</Property.Label>
              <Property.Value>
                <InlineCode variant="small">{model.matchPattern}</InlineCode>
              </Property.Value>
            </Property.Item>
          </Property.Table>
        </div>

        {/* Pricing */}
        <div className="rounded-md border border-grid-dimmed p-4">
          <Header2 className="mb-3">Pricing</Header2>
          <Property.Table>
            <Property.Item>
              <Property.Label>Input</Property.Label>
              <Property.Value>{formatModelPrice(model.inputPrice)} / 1M tokens</Property.Value>
            </Property.Item>
            <Property.Item>
              <Property.Label>Output</Property.Label>
              <Property.Value>{formatModelPrice(model.outputPrice)} / 1M tokens</Property.Value>
            </Property.Item>
          </Property.Table>
          {model.pricingTiers.length > 1 && (
            <div className="mt-4">
              <p className="mb-2 text-xs font-medium text-text-dimmed">All pricing tiers</p>
              {model.pricingTiers.map((tier) => (
                <div key={tier.name} className="mb-2 rounded border border-grid-dimmed p-2 text-xs">
                  <span className="font-medium text-text-bright">{tier.name}</span>
                  {tier.isDefault && (
                    <Badge variant="outline-rounded" className="ml-2">
                      default
                    </Badge>
                  )}
                  <div className="mt-1 space-y-0.5 text-text-dimmed">
                    {Object.entries(tier.prices).map(([usage, price]) => (
                      <div key={usage}>
                        {usage}: ${(price * 1_000_000).toFixed(4)} / 1M
                      </div>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>

      {/* Cost Estimator */}
      <CostEstimator
        inputPrice={model.inputPrice}
        outputPrice={model.outputPrice}
        defaultInputTokens={
          userMetrics.totalCalls > 0
            ? Math.round(userMetrics.totalInputTokens / userMetrics.totalCalls)
            : undefined
        }
        defaultOutputTokens={
          userMetrics.totalCalls > 0
            ? Math.round(userMetrics.totalOutputTokens / userMetrics.totalCalls)
            : undefined
        }
      />
    </div>
  );
}

// --- Global Metrics Tab ---

function GlobalMetricsTab({
  modelName,
  organizationId,
  projectId,
  environmentId,
}: {
  modelName: string;
  organizationId: string;
  projectId: string;
  environmentId: string;
}) {
  const widgetProps = {
    organizationId,
    projectId,
    environmentId,
    scope: "environment" as const,
    period: "7d",
    from: null,
    to: null,
  };

  return (
    <div className="space-y-4">
      {/* Big numbers */}
      <div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
        <div className="h-24">
          <MetricWidget
            widgetKey={`${modelName}-ttfc-p50`}
            title="p50 TTFC"
            query={`SELECT round(quantilesMerge(0.5)(ttfc_quantiles)[1], 0) AS ttfc_p50 FROM llm_models WHERE response_model = '${escapeTSQL(modelName)}'`}
            config={bignumberConfig("ttfc_p50", { aggregation: "avg", suffix: "ms" })}
            {...widgetProps}
          />
        </div>
        <div className="h-24">
          <MetricWidget
            widgetKey={`${modelName}-ttfc-p90`}
            title="p90 TTFC"
            query={`SELECT round(quantilesMerge(0.9)(ttfc_quantiles)[1], 0) AS ttfc_p90 FROM llm_models WHERE response_model = '${escapeTSQL(modelName)}'`}
            config={bignumberConfig("ttfc_p90", { aggregation: "avg", suffix: "ms" })}
            {...widgetProps}
          />
        </div>
        <div className="h-24">
          <MetricWidget
            widgetKey={`${modelName}-tps`}
            title="Tokens/sec (p50)"
            query={`SELECT round(quantilesMerge(0.5)(tps_quantiles)[1], 0) AS tps_p50 FROM llm_models WHERE response_model = '${escapeTSQL(modelName)}'`}
            config={bignumberConfig("tps_p50", { aggregation: "avg" })}
            {...widgetProps}
          />
        </div>
      </div>

      {/* Charts */}
      <div className="h-[300px]">
        <MetricWidget
          widgetKey={`${modelName}-ttfc-time`}
          title="TTFC over time"
          query={`SELECT timeBucket(), round(quantilesMerge(0.5)(ttfc_quantiles)[1], 0) AS ttfc_p50, round(quantilesMerge(0.9)(ttfc_quantiles)[1], 0) AS ttfc_p90 FROM llm_models WHERE response_model = '${escapeTSQL(modelName)}' GROUP BY timeBucket ORDER BY timeBucket`}
          config={chartConfig({
            chartType: "line",
            xAxisColumn: "timebucket",
            yAxisColumns: ["ttfc_p50", "ttfc_p90"],
            aggregation: "avg",
          })}
          {...widgetProps}
        />
      </div>

      <Callout variant="info">
        Aggregated across all Trigger.dev users. No tenant-specific data is exposed.
      </Callout>
    </div>
  );
}

// --- Your Usage Tab ---

function YourUsageTab({
  modelName,
  organizationId,
  projectId,
  environmentId,
}: {
  modelName: string;
  organizationId: string;
  projectId: string;
  environmentId: string;
}) {
  const widgetProps = {
    organizationId,
    projectId,
    environmentId,
    scope: "environment" as const,
    period: "7d",
    from: null,
    to: null,
  };

  return (
    <div className="space-y-4">
      {/* Big numbers */}
      <div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
        <div className="h-24">
          <MetricWidget
            widgetKey={`${modelName}-user-calls`}
            title="Total calls (7d)"
            query={`SELECT count() AS total_calls FROM llm_metrics WHERE response_model = '${escapeTSQL(modelName)}'`}
            config={bignumberConfig("total_calls", { abbreviate: true })}
            {...widgetProps}
          />
        </div>
        <div className="h-24">
          <MetricWidget
            widgetKey={`${modelName}-user-cost`}
            title="Total cost (7d)"
            query={`SELECT sum(total_cost) AS total_cost FROM llm_metrics WHERE response_model = '${escapeTSQL(modelName)}'`}
            config={bignumberConfig("total_cost", { aggregation: "sum" })}
            {...widgetProps}
          />
        </div>
        <div className="h-24">
          <MetricWidget
            widgetKey={`${modelName}-user-ttfc`}
            title="Avg TTFC"
            query={`SELECT round(avg(ms_to_first_chunk), 0) AS avg_ttfc FROM llm_metrics WHERE response_model = '${escapeTSQL(modelName)}' AND ms_to_first_chunk > 0`}
            config={bignumberConfig("avg_ttfc", { aggregation: "avg", suffix: "ms" })}
            {...widgetProps}
          />
        </div>
        <div className="h-24">
          <MetricWidget
            widgetKey={`${modelName}-user-tps`}
            title="Avg Tokens/sec"
            query={`SELECT round(avg(tokens_per_second), 0) AS avg_tps FROM llm_metrics WHERE response_model = '${escapeTSQL(modelName)}' AND tokens_per_second > 0`}
            config={bignumberConfig("avg_tps", { aggregation: "avg" })}
            {...widgetProps}
          />
        </div>
      </div>

      {/* Charts */}
      <div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
        <div className="h-[300px]">
          <MetricWidget
            widgetKey={`${modelName}-user-cost-time`}
            title="Cost over time"
            query={`SELECT timeBucket(), sum(total_cost) AS cost FROM llm_metrics WHERE response_model = '${escapeTSQL(modelName)}' GROUP BY timeBucket ORDER BY timeBucket`}
            config={chartConfig({
              chartType: "bar",
              xAxisColumn: "timebucket",
              yAxisColumns: ["cost"],
            })}
            {...widgetProps}
          />
        </div>
        <div className="h-[300px]">
          <MetricWidget
            widgetKey={`${modelName}-user-tokens-time`}
            title="Tokens over time"
            query={`SELECT timeBucket(), sum(input_tokens) AS input_tokens, sum(output_tokens) AS output_tokens FROM llm_metrics WHERE response_model = '${escapeTSQL(modelName)}' GROUP BY timeBucket ORDER BY timeBucket`}
            config={chartConfig({
              chartType: "bar",
              xAxisColumn: "timebucket",
              yAxisColumns: ["input_tokens", "output_tokens"],
            })}
            {...widgetProps}
          />
        </div>
      </div>

      {/* Task breakdown */}
      <div className="h-[300px]">
        <MetricWidget
          widgetKey={`${modelName}-user-tasks`}
          title="Cost by task"
          query={`SELECT task_identifier, count() AS calls, sum(total_cost) AS cost FROM llm_metrics WHERE response_model = '${escapeTSQL(modelName)}' GROUP BY task_identifier ORDER BY cost DESC LIMIT 20`}
          config={{ type: "table", prettyFormatting: true, sorting: [] }}
          {...widgetProps}
        />
      </div>
    </div>
  );
}
