// MailStatsTab — GH #873: mail statistics with history and a storage
// drilldown (Global → User → Domain → Mailbox). Data comes from
// GET /admin/mail/stats: `points` are raw sampled values (gauges),
// `rates` are per-interval deltas (counters), both keyed by whatever
// metric names Stalwart's exporter emits (it registers metrics lazily,
// so charts appear as traffic flows).
//
// Metric names are VERIFIED against a live exporter under real traffic —
// received mail lands as message_ingest_ham / message_ingest_spam (there
// is no plain "message_ingest"), tenant submissions count via
// queue_authenticated_message_queued, and outcomes via delivery_completed /
// delivery_dsn_perm_fail. Do not add a chart for a name you have not seen
// in mail_stats_samples.
import { useMemo, useState } from "react";
import { Alert, Card, Col, Radio, Row, Statistic, Table, Typography } from "antd";
import { useQuery } from "@tanstack/react-query";

import { apiClient } from "../../../apiClient";
import { Sparkline, type SparklinePoint } from "../../../components/Sparkline";

type Point = { t: string; v: number };

type MailStats = {
  points: Record<string, Point[]>;
  rates: Record<string, Point[]>;
  current: Record<string, number>;
  storage: {
    username: string;
    domain_name: string;
    email: string;
    usage_bytes: number;
    quota_bytes: number;
  }[];
};

const fmtBytes = (n: number): string => {
  if (n >= 1 << 30) return `${(n / (1 << 30)).toFixed(1)} GiB`;
  if (n >= 1 << 20) return `${(n / (1 << 20)).toFixed(1)} MiB`;
  if (n >= 1 << 10) return `${(n / (1 << 10)).toFixed(1)} KiB`;
  return `${n} B`;
};

const toSpark = (pts: Point[] | undefined): SparklinePoint[] =>
  (pts ?? []).map((p) => ({ x: p.t, y: p.v }));

// sumSeries merges rate series by timestamp (samples share tick times, but a
// metric only starts existing once Stalwart first increments it — union, not
// zip). Used for "received" = ham + spam classifications (GH #873 round 2;
// metric names verified against a live exporter, message_ingest_* register
// lazily per outcome).
const sumSeries = (...series: (Point[] | undefined)[]): SparklinePoint[] => {
  const byT = new Map<string, number>();
  for (const pts of series) {
    for (const p of pts ?? []) byT.set(p.t, (byT.get(p.t) ?? 0) + p.v);
  }
  return [...byT.entries()]
    .sort(([a], [b]) => (a < b ? -1 : 1))
    .map(([x, y]) => ({ x, y }));
};

// Total over the charted window (rates are per-interval deltas).
const sumTotal = (pts: Point[] | undefined): number =>
  (pts ?? []).reduce((acc, p) => acc + p.v, 0);

const ChartCard = ({
  title,
  data,
  formatY,
}: {
  title: string;
  data: SparklinePoint[];
  formatY?: (n: number) => string;
}) => (
  <Card size="small" title={title}>
    {data.length > 1 ? (
      <Sparkline data={data} width={320} height={64} formatY={formatY} />
    ) : (
      <Typography.Text type="secondary">
        Collecting — appears after a few samples.
      </Typography.Text>
    )}
  </Card>
);

export const MailStatsTab = () => {
  const [hours, setHours] = useState(24);
  const stats = useQuery<MailStats>({
    queryKey: ["admin", "mail", "stats", hours],
    queryFn: async () =>
      (await apiClient.get<MailStats>(`/admin/mail/stats?hours=${hours}`)).data,
    refetchInterval: 60_000,
  });

  const s = stats.data;

  // Storage drilldown: rows grouped client-side User → Domain → Mailbox.
  const storageTree = useMemo(() => {
    const users = new Map<
      string,
      { usage: number; domains: Map<string, { usage: number; boxes: MailStats["storage"] }> }
    >();
    for (const row of s?.storage ?? []) {
      const u = users.get(row.username) ?? { usage: 0, domains: new Map() };
      u.usage += row.usage_bytes;
      const d = u.domains.get(row.domain_name) ?? { usage: 0, boxes: [] };
      d.usage += row.usage_bytes;
      d.boxes.push(row);
      u.domains.set(row.domain_name, d);
      users.set(row.username, u);
    }
    return [...users.entries()].map(([username, u]) => ({
      key: username,
      username,
      usage: u.usage,
      domains: [...u.domains.entries()].map(([domain, d]) => ({
        key: `${username}/${domain}`,
        domain,
        usage: d.usage,
        boxes: d.boxes,
      })),
    }));
  }, [s?.storage]);

  const totalStorage = storageTree.reduce((acc, u) => acc + u.usage, 0);

  if (stats.isError) {
    return <Alert type="error" message="Failed to load mail statistics" />;
  }

  return (
    <>
      <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic title="Queue now" value={s?.current["queue_size"] ?? 0} />
          </Card>
        </Col>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic
              title="SMTP connections"
              value={s?.current["smtp_active_connections"] ?? 0}
            />
          </Card>
        </Col>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic
              title="IMAP connections"
              value={s?.current["imap_active_connections"] ?? 0}
            />
          </Card>
        </Col>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic title="Mailbox storage" value={fmtBytes(totalStorage)} />
          </Card>
        </Col>
      </Row>

      <Radio.Group
        value={hours}
        onChange={(e) => setHours(e.target.value)}
        style={{ marginBottom: 16 }}
        options={[
          { label: "24h", value: 24 },
          { label: "7d", value: 168 },
          { label: "30d", value: 720 },
        ]}
        optionType="button"
      />

      <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic
              title={`Received (${hours >= 168 ? `${hours / 24}d` : `${hours}h`})`}
              value={sumTotal(s?.rates["message_ingest_ham"]) + sumTotal(s?.rates["message_ingest_spam"])}
            />
          </Card>
        </Col>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic
              title={`Sent (${hours >= 168 ? `${hours / 24}d` : `${hours}h`})`}
              value={sumTotal(s?.rates["queue_authenticated_message_queued"])}
            />
          </Card>
        </Col>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic
              title={`Spam blocked (${hours >= 168 ? `${hours / 24}d` : `${hours}h`})`}
              value={sumTotal(s?.rates["message_ingest_spam"])}
            />
          </Card>
        </Col>
        <Col xs={12} md={6}>
          <Card size="small">
            <Statistic
              title={`Delivery failures (${hours >= 168 ? `${hours / 24}d` : `${hours}h`})`}
              value={sumTotal(s?.rates["delivery_dsn_perm_fail"])}
            />
          </Card>
        </Col>
      </Row>

      <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
        <Col xs={24} md={8}>
          <ChartCard
            title="Messages received"
            data={sumSeries(
              s?.rates["message_ingest_ham"],
              s?.rates["message_ingest_spam"],
            )}
          />
        </Col>
        <Col xs={24} md={8}>
          <ChartCard
            title="Messages sent"
            data={toSpark(s?.rates["queue_authenticated_message_queued"])}
          />
        </Col>
        <Col xs={24} md={8}>
          <ChartCard
            title="Spam blocked"
            data={toSpark(s?.rates["message_ingest_spam"])}
          />
        </Col>
      </Row>

      <Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
        <Col xs={24} md={8}>
          <ChartCard
            title="Deliveries completed"
            data={toSpark(s?.rates["delivery_completed"])}
          />
        </Col>
        <Col xs={24} md={8}>
          <ChartCard title="Queue size" data={toSpark(s?.points["queue_size"])} />
        </Col>
        <Col xs={24} md={8}>
          <ChartCard
            title="Successful logins"
            data={toSpark(s?.rates["auth_success"])}
          />
        </Col>
      </Row>

      <Card size="small" title="Storage drilldown" loading={stats.isLoading}>
        <Table
          size="small"
          rowKey="key"
          pagination={false}
          dataSource={storageTree}
          columns={[
            { title: "User", dataIndex: "username" },
            {
              title: "Usage",
              dataIndex: "usage",
              render: (v: number) => fmtBytes(v),
              width: 140,
            },
          ]}
          expandable={{
            expandedRowRender: (user) => (
              <Table
                size="small"
                rowKey="key"
                pagination={false}
                dataSource={user.domains}
                columns={[
                  { title: "Domain", dataIndex: "domain" },
                  {
                    title: "Usage",
                    dataIndex: "usage",
                    render: (v: number) => fmtBytes(v),
                    width: 140,
                  },
                ]}
                expandable={{
                  expandedRowRender: (dom) => (
                    <Table
                      size="small"
                      rowKey="email"
                      pagination={false}
                      dataSource={dom.boxes}
                      columns={[
                        { title: "Mailbox", dataIndex: "email" },
                        {
                          title: "Usage",
                          dataIndex: "usage_bytes",
                          render: (v: number) => fmtBytes(v),
                          width: 140,
                        },
                        {
                          title: "Quota",
                          dataIndex: "quota_bytes",
                          render: (v: number) => fmtBytes(v),
                          width: 140,
                        },
                      ]}
                    />
                  ),
                }}
              />
            ),
          }}
        />
      </Card>
    </>
  );
};
