import { useQuery } from "@tanstack/react-query";
import {
  Activity,
  AlertTriangle,
  CheckCircle2,
  Database,
  LayoutDashboard,
  ShieldCheck,
  Users,
} from "lucide-react";
import { type ReactNode, useMemo, useState } from "react";
import {
  OverviewAxisNotes,
  OverviewMetric,
  OverviewSelectionChips,
} from "../../../../common/core/components/overview";
import { RoleGuard } from "../../components/auth/RoleGuard";
import {
  type DataIntegrityStatus,
  type RPUsageDailyMetric,
  type RPUsagePeriod,
  type TenantSummary,
  fetchAdminOverviewStats,
  fetchAdminRPUsageDaily,
  fetchAllTenants,
  fetchDataIntegrityReport,
} from "../../lib/adminApi";
import { t } from "../../lib/i18n";

type DailyPoint = {
  date: string;
  loginRequests: number;
  otherRequests: number;
};

type SeriesSummary = {
  key: string;
  clientLabel: string;
  loginRequests: number;
  uniqueSubjects: number;
};

function summarizeDaily(rows: RPUsageDailyMetric[]): DailyPoint[] {
  const byDate = new Map<string, DailyPoint>();
  for (const row of rows) {
    const current =
      byDate.get(row.date) ??
      ({
        date: row.date,
        loginRequests: 0,
        otherRequests: 0,
      } satisfies DailyPoint);
    current.loginRequests += row.loginRequests;
    current.otherRequests += row.otherRequests;
    byDate.set(row.date, current);
  }
  return Array.from(byDate.values()).sort((a, b) =>
    a.date.localeCompare(b.date),
  );
}

function summarizeSeries(rows: RPUsageDailyMetric[]): SeriesSummary[] {
  const bySeries = new Map<string, SeriesSummary>();
  for (const row of rows) {
    const key = row.clientId;
    const current =
      bySeries.get(key) ??
      ({
        key,
        clientLabel: row.clientName || row.clientId,
        loginRequests: 0,
        uniqueSubjects: 0,
      } satisfies SeriesSummary);
    current.loginRequests += row.loginRequests;
    current.uniqueSubjects = Math.max(
      current.uniqueSubjects,
      row.uniqueSubjects,
    );
    bySeries.set(key, current);
  }
  return Array.from(bySeries.values())
    .sort((a, b) => b.loginRequests - a.loginRequests)
    .slice(0, 5);
}

function parseDateParts(date: string) {
  const parts = date.split("-");
  if (parts.length === 3) {
    return {
      year: Number(parts[0]),
      month: Number(parts[1]),
      day: Number(parts[2]),
      monthText: parts[1],
      dayText: parts[2],
    };
  }
  return null;
}

function getISOWeekNumber(year: number, month: number, day: number) {
  const date = new Date(Date.UTC(year, month - 1, day));
  const dayOfWeek = date.getUTCDay() || 7;
  date.setUTCDate(date.getUTCDate() + 4 - dayOfWeek);
  const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
  return Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
}

function getISOWeekThursday(year: number, month: number, day: number) {
  const date = new Date(Date.UTC(year, month - 1, day));
  const dayOfWeek = date.getUTCDay() || 7;
  date.setUTCDate(date.getUTCDate() + 4 - dayOfWeek);
  return date;
}

function formatPeriodLabel(date: string, period: RPUsagePeriod) {
  const parts = parseDateParts(date);
  if (!parts) {
    return date;
  }
  if (period === "month") {
    return `${parts.monthText}월`;
  }
  if (period === "week") {
    const weekNumber = String(
      getISOWeekNumber(parts.year, parts.month, parts.day),
    ).padStart(2, "0");
    const weekThursday = getISOWeekThursday(parts.year, parts.month, parts.day);
    const weekMonth = weekThursday.getUTCMonth() + 1;
    const weekDay = weekThursday.getUTCDate();
    const weekMonthText = String(weekMonth).padStart(2, "0");
    const weekOfMonth = Math.min(5, Math.max(1, Math.ceil(weekDay / 7)));
    return `${weekNumber}(${weekMonthText}월${weekOfMonth}주)`;
  }
  return `${parts.monthText}.${parts.dayText}`;
}

function formatOverviewDateTime(value?: string) {
  if (!value) return "-";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return new Intl.DateTimeFormat("ko-KR", {
    dateStyle: "medium",
    timeStyle: "short",
  }).format(date);
}

function integrityStatusText(status: DataIntegrityStatus) {
  switch (status) {
    case "pass":
      return t("ui.admin.integrity.status.pass", "정상");
    case "warning":
      return t("ui.admin.integrity.status.warning", "주의");
    default:
      return t("ui.admin.integrity.status.fail", "실패");
  }
}

function integrityStatusClass(status: DataIntegrityStatus) {
  switch (status) {
    case "pass":
      return "text-emerald-700 dark:text-emerald-300";
    case "warning":
      return "text-amber-700 dark:text-amber-300";
    default:
      return "text-destructive";
  }
}

function IntegrityOverviewSummary() {
  const { data, isError } = useQuery({
    queryKey: ["admin-overview-integrity"],
    queryFn: fetchDataIntegrityReport,
    retry: false,
  });

  if (isError) {
    return (
      <section className="border-t border-border/60 pt-4">
        <div className="flex items-center gap-2 text-sm text-muted-foreground">
          <AlertTriangle size={16} />
          <span>
            {t(
              "ui.admin.integrity.fetch_error",
              "정합성 최종 검증 결과를 불러오지 못했습니다.",
            )}
          </span>
        </div>
      </section>
    );
  }

  if (!data) {
    return null;
  }

  return (
    <section className="border-t border-border/60 pt-4">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div className="flex items-center gap-2">
          {data.status === "pass" ? (
            <CheckCircle2 size={18} className="text-emerald-600" />
          ) : (
            <AlertTriangle size={18} className="text-amber-600" />
          )}
          <h3 className="text-lg font-bold flex items-center gap-2">
            {t(
              "ui.admin.integrity.summary.title",
              "정합성 최종 검증",
            )}
          </h3>
        </div>
        <div className="flex flex-wrap items-center gap-3 text-sm">
          <span
            className={`font-semibold ${integrityStatusClass(data.status)}`}
          >
            {integrityStatusText(data.status)}
          </span>
          <span className="tabular-nums">
            {t("ui.admin.integrity.summary.failures_text", "실패 {{count}}건", {
              count: data.summary.failures,
            })}
          </span>
          <span className="text-muted-foreground">
            {formatOverviewDateTime(data.checkedAt)}
          </span>
        </div>
      </div>
      <div className="mt-3 grid gap-2 text-sm sm:grid-cols-2">
        {data.sections.map((section) => (
          <div
            key={section.key}
            className="flex items-center justify-between gap-3 rounded border border-border/60 px-3 py-2"
          >
            <span>{integritySectionLabel(section.key, section.label)}</span>
            <span
              className={`font-medium ${integrityStatusClass(section.status)}`}
            >
              {integrityStatusText(section.status)}
            </span>
          </div>
        ))}
      </div>
    </section>
  );
}

function integritySectionLabel(key: string, fallback: string) {
  switch (key) {
    case "tenant_integrity":
      return t("ui.admin.integrity.section.tenant_integrity", fallback);
    case "user_integrity":
      return t("ui.admin.integrity.section.user_integrity", fallback);
    default:
      return fallback;
  }
}

function RPUsageMixedChart({
  rows,
  periodControls,
  filters,
  period,
}: {
  rows: RPUsageDailyMetric[];
  periodControls: ReactNode;
  filters: ReactNode;
  period: RPUsagePeriod;
}) {
  const daily = summarizeDaily(rows);
  const series = summarizeSeries(rows);
  const chartWidth = 720;
  const chartHeight = 230;
  const padX = 48;
  const padTop = 32;
  const padBottom = 34;
  const innerWidth = chartWidth - padX * 2;
  const innerHeight = chartHeight - padTop - padBottom;
  const maxValue = Math.max(
    1,
    ...daily.map((point) => point.loginRequests + point.otherRequests),
    ...daily.map((point) => point.loginRequests),
  );
  const slot = daily.length > 0 ? innerWidth / daily.length : innerWidth;
  const barWidth = Math.min(28, Math.max(10, slot * 0.42));
  const y = (value: number) =>
    padTop + innerHeight - (value / maxValue) * innerHeight;
  const x = (index: number) => padX + slot * index + slot / 2;
  const linePoints = daily
    .map((point, index) => `${x(index)},${y(point.loginRequests)}`)
    .join(" ");

  return (
    <section className="space-y-3">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div className="space-y-1">
          <h3 className="text-lg font-bold flex items-center gap-2">
            {t("ui.admin.overview.chart.title", "회사별 앱별 로그인 요청 현황")}
          </h3>
          <p className="text-sm text-muted-foreground">
            {t(
              "ui.admin.overview.chart.description",
              "전체 또는 선택한 조직 기준으로 그래프를 확인합니다.",
            )}
          </p>
        </div>
        {periodControls}
      </div>

      {filters}

      {daily.length === 0 ? (
        <div className="flex min-h-[210px] items-center justify-center text-sm text-muted-foreground">
          표시할 RP 이용 집계가 없습니다.
        </div>
      ) : (
        <div className="space-y-3">
          <div className="overflow-x-auto">
            <svg
              role="img"
              aria-label="일 단위 RP 요청 현황"
              viewBox={`0 0 ${chartWidth} ${chartHeight}`}
              className="h-[235px] min-w-[720px] w-full"
            >
              <title>일 단위 RP 요청 현황</title>
              {[0, 0.25, 0.5, 0.75, 1].map((ratio) => {
                const gridY = padTop + innerHeight * ratio;
                const label = Math.round(maxValue * (1 - ratio));
                return (
                  <g key={ratio}>
                    <line
                      x1={padX}
                      x2={chartWidth - padX}
                      y1={gridY}
                      y2={gridY}
                      stroke="currentColor"
                      className="text-border"
                      strokeWidth="1"
                    />
                    <text
                      x={padX - 12}
                      y={gridY + 4}
                      textAnchor="end"
                      className="fill-muted-foreground text-[11px]"
                    >
                      {label}
                    </text>
                  </g>
                );
              })}
              {daily.map((point, index) => {
                const center = x(index);
                const otherHeight =
                  (point.otherRequests / maxValue) * innerHeight;
                return (
                  <g key={point.date}>
                    <rect
                      x={center - barWidth / 2}
                      y={padTop + innerHeight - otherHeight}
                      width={barWidth}
                      height={otherHeight}
                      rx="3"
                      className="fill-sky-500/70"
                    />
                    <text
                      x={center}
                      y={chartHeight - 12}
                      textAnchor="middle"
                      className="fill-muted-foreground text-[11px]"
                    >
                      {formatPeriodLabel(point.date, period)}
                    </text>
                  </g>
                );
              })}
              <polyline
                points={linePoints}
                fill="none"
                className="stroke-emerald-500"
                strokeWidth="3"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
              {daily.map((point, index) => (
                <circle
                  key={`${point.date}-login`}
                  cx={x(index)}
                  cy={y(point.loginRequests)}
                  r="4"
                  className="fill-emerald-500 stroke-background"
                  strokeWidth="2"
                />
              ))}
            </svg>
          </div>
          <OverviewAxisNotes
            xAxisLabel={t("ui.common.chart.axis.x", "X축: 기간")}
            yAxisLabel={t("ui.common.chart.axis.y", "Y축: 로그인 요청 수")}
          />
        </div>
      )}

      {series.length > 0 && (
        <div className="grid gap-x-6 gap-y-2 border-t border-border/60 pt-2 text-xs md:grid-cols-2 xl:grid-cols-3">
          {series.map((item) => (
            <div
              key={item.key}
              className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1"
            >
              <span className="font-medium">{item.clientLabel}</span>
              <span className="whitespace-nowrap tabular-nums text-muted-foreground">
                {t(
                  "ui.common.chart.series_summary.login_users",
                  "로그인 {{login}} / 사용자 {{subjects}}",
                  {
                    login: item.loginRequests.toLocaleString(),
                    subjects: item.uniqueSubjects.toLocaleString(),
                  },
                )}
              </span>
            </div>
          ))}
        </div>
      )}
    </section>
  );
}

function GlobalOverviewPage() {
  const [period, setPeriod] = useState<RPUsagePeriod>("day");
  const [selectedTenantIds, setSelectedTenantIds] = useState<string[]>([]);
  const usageDays = period === "day" ? 14 : period === "week" ? 84 : 90;
  const statsQuery = useQuery({
    queryKey: ["admin-overview-stats"],
    queryFn: fetchAdminOverviewStats,
    retry: false,
  });
  const tenantsQuery = useQuery({
    queryKey: ["admin-overview-tenant-options"],
    queryFn: () => fetchAllTenants(),
    retry: false,
  });
  const tenantOptions = useMemo(() => {
    return (tenantsQuery.data?.items ?? []).filter(
      (tenant) => tenant.type === "COMPANY",
    );
  }, [tenantsQuery.data?.items]);
  const usageQuery = useQuery({
    queryKey: ["admin-rp-usage-daily", usageDays, period],
    queryFn: () =>
      fetchAdminRPUsageDaily({
        days: usageDays,
        period,
      }),
    retry: false,
  });
  const stats = statsQuery.data;
  const visibleTenantCount = tenantsQuery.data?.items.length;
  const usageRows = usageQuery.data?.items ?? [];
  const filteredUsageRows = useMemo(() => {
    if (selectedTenantIds.length === 0) {
      return usageRows;
    }
    const selectedSet = new Set(selectedTenantIds);
    return usageRows.filter((row) => selectedSet.has(row.tenantId));
  }, [selectedTenantIds, usageRows]);
  const metric = (value: number | undefined) =>
    value === undefined ? "-" : value.toLocaleString();
  const periodControls = (
    <div className="flex h-8 items-center gap-1" aria-label="집계 단위">
      {[
        ["day", t("ui.common.chart.period.day", "일")],
        ["week", t("ui.common.chart.period.week", "주")],
        ["month", t("ui.common.chart.period.month", "월")],
      ].map(([value, label]) => (
        <button
          key={value}
          type="button"
          aria-pressed={period === value}
          onClick={() => setPeriod(value as RPUsagePeriod)}
          className={`h-8 rounded px-3 text-xs font-medium transition-colors ${
            period === value
              ? "bg-primary text-primary-foreground"
              : "bg-muted/60 hover:bg-muted"
          }`}
        >
          {label}
        </button>
      ))}
    </div>
  );
  const chartFilters = (
    <div>
      <OverviewSelectionChips
        allLabel="전체"
        options={tenantOptions.map((tenant) => ({
          id: tenant.id,
          label: `${tenant.name} (${tenant.slug})`,
        }))}
        selectedIds={selectedTenantIds}
        onSelectAll={() => setSelectedTenantIds([])}
        onToggle={(tenantId) => {
          setSelectedTenantIds((current) =>
            current.includes(tenantId)
              ? current.filter((item) => item !== tenantId)
              : [...current, tenantId],
          );
        }}
      />
    </div>
  );

  return (
    <div className="space-y-4 animate-in fade-in duration-500">
      <div className="flex flex-wrap items-start justify-between gap-4">
        <div className="flex min-w-0 items-start gap-3">
          <div className="mt-1 flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-primary/15 bg-primary/10 text-primary">
            <LayoutDashboard size={20} />
          </div>
          <div className="space-y-1">
            <h2 className="text-3xl font-semibold">
              {t("ui.common.overview.title", "운영 현황")}
            </h2>
            <p className="text-sm text-muted-foreground">
              {t(
                "msg.admin.overview.description",
                "시스템 전반의 주요 현황을 확인하고 관리합니다.",
              )}
            </p>
          </div>
        </div>
      </div>

      <div className="flex flex-wrap items-center gap-x-6 gap-y-2 border-y border-border/60 py-2">
        <RoleGuard roles={["super_admin"]}>
          <OverviewMetric
            icon={<Users size={14} />}
            label={t(
              "ui.admin.overview.summary.total_tenants",
              "전체 테넌트 수",
            )}
            value={metric(visibleTenantCount ?? stats?.totalTenants)}
          />
          <OverviewMetric
            icon={<ShieldCheck size={14} />}
            label={t(
              "ui.admin.overview.summary.oidc_clients",
              "OIDC 클라이언트",
            )}
            value={metric(stats?.oidcClients)}
          />
          <OverviewMetric
            icon={<Users size={14} />}
            label={t("ui.admin.overview.summary.total_users", "전체 사용자 수")}
            value={metric(stats?.totalUsers)}
          />
        </RoleGuard>
        <OverviewMetric
          icon={<Activity size={14} />}
          label={t(
            "ui.admin.overview.summary.audit_events_24h",
            "24시간 이벤트",
          )}
          value={metric(stats?.auditEvents24h)}
        />
        <OverviewMetric
          icon={<Database size={14} />}
          label={t("ui.admin.overview.summary.policy_gate", "정책 상태")}
          value="Active"
        />
      </div>

      {usageQuery.isError ? (
        <section className="space-y-2">
          <div className="flex flex-wrap items-start justify-between gap-3">
            <div className="space-y-1">
              <h3 className="text-lg font-bold flex items-center gap-2">
                {t(
                  "ui.admin.overview.chart.title",
                  "회사별 앱별 로그인 요청 현황",
                )}
              </h3>
              <p className="text-sm text-muted-foreground">
                {t(
                  "ui.admin.overview.chart.description",
                  "전체 또는 선택한 조직 기준으로 그래프를 확인합니다.",
                )}
              </p>
            </div>
            {periodControls}
          </div>
          {chartFilters}
          <div className="text-sm text-muted-foreground">
            RP 이용 통계 Query API 응답을 확인할 수 없습니다. backend 재시작
            이후 `rp_usage_daily_aggregate`가 준비되면 이 영역에 일 단위
            그래프가 표시됩니다.
          </div>
        </section>
      ) : (
        <RPUsageMixedChart
          rows={filteredUsageRows}
          periodControls={periodControls}
          filters={chartFilters}
          period={period}
        />
      )}

      <RoleGuard roles={["super_admin"]}>
        <IntegrityOverviewSummary />
      </RoleGuard>
    </div>
  );
}

export default GlobalOverviewPage;
