/**
 * GPU Instances Tab — per-instance compute usage view.
 *
 * Layout mirrors the existing Token tab:
 *   1. Top filter bar (date range + scope)
 *   2. KPI row (GPU-Hours / GPU-Minutes / Instances / GPU Types / Active Users)
 *   3. Daily bar chart with metric + group_by switches
 *   4. Bottom tab table grouped by GPU Type / Instance / User
 *
 * Talks to the new ``/usage/gpu-instances/{meta,breakdown}`` endpoints.
 */
import useCoolColors from '@/hooks/use-cool-colors';
import { formatLargeNumber } from '@/utils';
import { SimpleCard } from '@gpustack/core-ui';
import { useAccess } from '@umijs/max';
import { Table, Tabs } from 'antd';
import dayjs from 'dayjs';
import React, { useEffect, useMemo, useState } from 'react';
import {
  queryGpuInstancesBreakdown,
  ResourceBreakdownItem,
  ResourceBreakdownRequest,
  ResourceBreakdownResponse
} from '../apis/resource';
import useResourceMeta from '../hooks/use-resource-meta';
import {
  bucketKey,
  generateBucketRange,
  Granularity
} from '../utils/time-buckets';
import MetricChartCard from './metric-chart-card';
import ResourceExportData from './resource-export-data';
import ResourceFilterBar from './resource-filter-bar';

type Scope = 'self' | 'all';
type Metric = 'gpu_hours' | 'instance_hours';
type GroupKey = 'gpu_type' | 'instance' | 'user';

const METRIC_OPTIONS: { value: Metric; label: string }[] = [
  { value: 'gpu_hours', label: 'GPU Hours' },
  { value: 'instance_hours', label: 'Instance Hours' }
];

const TABLE_TABS: { key: GroupKey; label: string }[] = [
  { key: 'gpu_type', label: 'Instance Types' },
  { key: 'instance', label: 'Instances' },
  { key: 'user', label: 'Users' }
];

const GpuInstancesTab: React.FC = () => {
  const access = useAccess();
  // ``useCoolColors`` returns a memoized factory; resolve it once into a
  // fixed 5-slot palette here so the rest of the component reads as
  // array access.
  const coolColors = useCoolColors()(5);

  // No All/My dropdown (matches the Tokens tab): managers see the org-wide
  // view and narrow it with the user filter, others only their own rows.
  const canManageUsers = !!access.canSeeOrgAdmin;
  const scope: Scope = canManageUsers ? 'all' : 'self';

  const [dateRange, setDateRange] = useState<[dayjs.Dayjs, dayjs.Dayjs]>([
    dayjs().subtract(29, 'day'),
    dayjs()
  ]);
  const [selectedUsers, setSelectedUsers] = useState<number[]>([]);
  const [selectedInstances, setSelectedInstances] = useState<number[]>([]);
  const [refreshKey, setRefreshKey] = useState(0);
  const [metric, setMetric] = useState<Metric>('gpu_hours');
  const [granularity, setGranularity] = useState<Granularity>('day');
  // ``null`` group_by = no row grouping, just the summary KPIs.
  // The chart needs the ``date`` group; tables use the active table tab.
  const [activeTableTab, setActiveTableTab] = useState<GroupKey>('gpu_type');

  const { creators: userOptions, instances: instanceOptions } =
    useResourceMeta(scope);

  // Two independent fetches: one for the daily chart (group_by=date),
  // one for the table (group_by=tab key). Both reuse the same date /
  // scope filters so the views stay in sync.
  const [chartData, setChartData] = useState<ResourceBreakdownResponse | null>(
    null
  );
  const [tableData, setTableData] = useState<ResourceBreakdownResponse | null>(
    null
  );
  const [tablePage, setTablePage] = useState(1);

  const baseRequest = (): Omit<ResourceBreakdownRequest, 'group_by'> => ({
    start_date: dateRange[0].format('YYYY-MM-DD'),
    end_date: dateRange[1].format('YYYY-MM-DD'),
    scope,
    granularity,
    filters:
      selectedUsers.length || selectedInstances.length
        ? {
            ...(selectedUsers.length ? { creator_ids: selectedUsers } : {}),
            ...(selectedInstances.length
              ? { instance_ids: selectedInstances }
              : {})
          }
        : undefined,
    page: 1,
    perPage: 50
  });

  const fetchChart = async () => {
    try {
      const data = await queryGpuInstancesBreakdown({
        ...baseRequest(),
        group_by: 'date'
      });
      setChartData(data);
    } catch {
      // Network/auth errors surface via the global request interceptor;
      // keep the previous chart so the UI doesn't flash empty.
    }
  };

  const fetchTable = async () => {
    try {
      const data = await queryGpuInstancesBreakdown({
        ...baseRequest(),
        group_by: activeTableTab,
        page: tablePage
      });
      setTableData(data);
    } catch {
      // Same rationale as fetchChart.
    }
  };

  useEffect(() => {
    fetchChart();
  }, [dateRange, selectedUsers, selectedInstances, granularity, refreshKey]);

  useEffect(() => {
    fetchTable();
  }, [
    dateRange,
    selectedUsers,
    selectedInstances,
    activeTableTab,
    tablePage,
    refreshKey
  ]);

  // KPI summary cards — pull from the chart summary since both queries
  // return the same scope-wide totals.
  const summary = chartData?.summary;
  const summaryCards = useMemo(
    () => [
      {
        label: formatLargeNumber(
          Math.round((summary?.gpu_hours ?? 0) * 10) / 10
        ) as string,
        value: 'GPU Hours',
        color: coolColors[0]
      },
      {
        label: formatLargeNumber(
          Math.round((summary?.instance_hours ?? 0) * 10) / 10
        ) as string,
        value: 'Instance Hours',
        color: coolColors[1]
      },
      {
        label: (summary?.active_instances ?? 0).toString(),
        value: 'Active Instances',
        color: coolColors[2]
      },
      {
        label: (summary?.active_users ?? 0).toString(),
        value: 'Active Users',
        color: coolColors[3]
      }
    ],
    [summary, coolColors]
  );

  // Build chart series — single series of the selected metric, plotted
  // along the contiguous date range.
  const dataByDate = useMemo(() => {
    const map = new Map<string, number>();
    chartData?.items?.forEach((item) => {
      if (!item.date) return;
      map.set(bucketKey(item.date, granularity), Number(item[metric] ?? 0));
    });
    return map;
  }, [chartData, metric, granularity]);
  const xAxis = useMemo(() => {
    const keys = new Set(
      generateBucketRange(
        dateRange[0].format('YYYY-MM-DD'),
        dateRange[1].format('YYYY-MM-DD'),
        granularity
      )
    );
    dataByDate.forEach((_v, k) => keys.add(k));
    return Array.from(keys).sort();
  }, [dataByDate, dateRange, granularity]);

  const seriesData = [
    {
      name: METRIC_OPTIONS.find((m) => m.value === metric)?.label || metric,
      data: xAxis.map((d) => dataByDate.get(d) ?? 0),
      color: coolColors[0]
    }
  ];

  // Table columns adapt to the active tab.
  const tableColumns = useMemo(() => {
    const baseValueCols = [
      {
        title: 'GPU Hours',
        dataIndex: 'gpu_hours',
        key: 'gpu_hours',
        render: (v: number) => (v ?? 0).toFixed(2)
      },
      {
        title: 'Instance Hours',
        dataIndex: 'instance_hours',
        key: 'instance_hours',
        render: (v: number) => (v ?? 0).toFixed(2)
      }
    ];
    if (activeTableTab === 'gpu_type') {
      return [
        { title: 'Instance Type', dataIndex: 'gpu_type', key: 'gpu_type' },
        ...baseValueCols,
        {
          title: 'Active Instances',
          dataIndex: 'active_instances',
          key: 'active_instances'
        },
        { title: 'Last Active', dataIndex: 'last_active', key: 'last_active' }
      ];
    }
    if (activeTableTab === 'instance') {
      return [
        { title: 'Instance', dataIndex: 'instance_name', key: 'instance_name' },
        { title: 'Instance Type', dataIndex: 'gpu_type', key: 'gpu_type' },
        ...baseValueCols,
        { title: 'Last Active', dataIndex: 'last_active', key: 'last_active' }
      ];
    }
    // user tab
    return [
      { title: 'User', dataIndex: 'user_name', key: 'user_name' },
      ...baseValueCols,
      { title: 'Last Active', dataIndex: 'last_active', key: 'last_active' }
    ];
  }, [activeTableTab]);

  const tableRows: ResourceBreakdownItem[] = tableData?.items ?? [];

  // Export opens a preview modal (matches the Tokens tab): re-filter + preview
  // the rows, then download. "Chart" = the by-date trend, "Table" = the active
  // bottom-table grouping.
  const [exportMode, setExportMode] = useState<'chart' | 'table' | null>(null);
  const dateSuffix = `${dateRange[0].format('YYYY-MM-DD')}_${dateRange[1].format(
    'YYYY-MM-DD'
  )}`;

  const chartExportColumns = [
    { title: 'Date', dataIndex: 'date', key: 'date' },
    {
      title: 'GPU Hours',
      dataIndex: 'gpu_hours',
      key: 'gpu_hours',
      render: (v: number) => (v ?? 0).toFixed(2)
    },
    {
      title: 'Instance Hours',
      dataIndex: 'instance_hours',
      key: 'instance_hours',
      render: (v: number) => (v ?? 0).toFixed(2)
    },
    {
      title: 'Active Instances',
      dataIndex: 'active_instances',
      key: 'active_instances'
    },
    { title: 'Active Users', dataIndex: 'active_users', key: 'active_users' }
  ];

  const tabLabel = TABLE_TABS.find((t) => t.key === activeTableTab)?.label;
  const exportConfig =
    exportMode === 'chart'
      ? {
          groupBy: 'date' as const,
          columns: chartExportColumns,
          fileName: `gpu-instances_chart_${dateSuffix}.xlsx`,
          sheetName: 'GPU Instances'
        }
      : {
          groupBy: activeTableTab,
          columns: tableColumns,
          fileName: `gpu-instances_${activeTableTab}_${dateSuffix}.xlsx`,
          sheetName: tabLabel || 'gpu-instances'
        };

  return (
    <div>
      {/* Top filter row */}
      <ResourceFilterBar
        value={dateRange}
        onChange={(dates) => {
          setDateRange(dates);
          setTablePage(1);
        }}
        canManageUsers={canManageUsers}
        userOptions={userOptions}
        selectedUsers={selectedUsers}
        onUsersChange={(ids) => {
          setSelectedUsers(ids);
          setTablePage(1);
        }}
        resourceFilter={{
          options: instanceOptions,
          value: selectedInstances,
          onChange: (ids) => {
            setSelectedInstances(ids);
            setTablePage(1);
          },
          placeholder: 'Filter by instance'
        }}
        onRefresh={() => setRefreshKey((k) => k + 1)}
        onExportChart={() => setExportMode('chart')}
        onExportTable={() => setExportMode('table')}
      />

      {/* KPI cards */}
      <div style={{ height: 24 }} />
      <div style={{ marginBottom: 24 }}>
        <SimpleCard
          dataList={summaryCards}
          height={80}
          styles={{
            item: {
              backgroundColor: 'var(--ant-color-fill-quaternary)',
              borderRadius: 6
            }
          }}
        />
      </div>

      {/* Daily trend chart */}
      <div style={{ marginBottom: 24 }}>
        <MetricChartCard
          metric={metric}
          metricOptions={METRIC_OPTIONS}
          granularity={granularity}
          onMetricChange={(v) => setMetric(v as Metric)}
          onGranularityChange={(v) => setGranularity(v as Granularity)}
          seriesData={seriesData}
          xAxisData={xAxis}
        />
      </div>

      {/* Bottom tabs + table */}
      <Tabs
        activeKey={activeTableTab}
        onChange={(k) => {
          setActiveTableTab(k as GroupKey);
          setTablePage(1);
        }}
        items={TABLE_TABS.filter(
          (t) => t.key !== 'user' || scope === 'all'
        ).map((t) => ({
          key: t.key,
          label: t.label,
          children: (
            <Table
              rowKey={(row) =>
                `${row.gpu_type ?? ''}|${row.instance_id ?? ''}|${row.user_id ?? ''}`
              }
              dataSource={tableRows}
              columns={tableColumns as any}
              pagination={{
                current: tablePage,
                pageSize: tableData?.pagination.perPage ?? 50,
                total: tableData?.pagination.total ?? 0,
                onChange: (p) => setTablePage(p)
              }}
            />
          )
        }))}
      />

      <ResourceExportData
        open={exportMode !== null}
        onCancel={() => setExportMode(null)}
        title={
          exportMode === 'chart'
            ? 'Export Chart Data'
            : `Export Table Data — ${tabLabel}`
        }
        queryFn={queryGpuInstancesBreakdown}
        groupBy={exportConfig.groupBy}
        columns={exportConfig.columns}
        fileName={exportConfig.fileName}
        sheetName={exportConfig.sheetName}
        scope={scope}
        canManageUsers={canManageUsers}
        userOptions={userOptions}
        resourceFilter={{
          options: instanceOptions,
          placeholder: 'Filter by instance',
          key: 'instance_ids'
        }}
        initialDateRange={dateRange}
        initialSelectedUsers={selectedUsers}
        initialSelectedResources={selectedInstances}
      />
    </div>
  );
};

export default GpuInstancesTab;
