import { Button, Card, Empty, Flex, Space, Tag, Typography } from 'antd';
import { useEffect, useMemo, useState } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import {
  CartesianGrid,
  Legend,
  Line,
  LineChart,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from 'recharts';
import './planBoard.css';
import { fetchPlanItems } from './api';
import type { PlanItem } from './types';

const { Paragraph, Text, Title } = Typography;

export const DAILY_CHART_DAYS = 7;
export const WEEKLY_CHART_WEEKS = 8;
const CHART_REFRESH_INTERVAL_MS = 5000;

type PerformanceChartDatum = {
  label: string;
  registered: number;
  jangsing: number;
  completed: number;
  merged: number;
};

type CurrentPlanSnapshot = {
  total: number;
  registered: number;
  inProgress: number;
  functionChecked: number;
  completed: number;
  merged: number;
};

export function PlanBoardChartsPage() {
  const [items, setItems] = useState<PlanItem[]>([]);
  const [loading, setLoading] = useState(false);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  useEffect(() => {
    let cancelled = false;

    const load = async (showLoading = true) => {
      if (showLoading) {
        setLoading(true);
      }

      try {
        const nextItems = await fetchPlanItems('all');

        if (cancelled) {
          return;
        }

        setItems(nextItems);
        setErrorMessage(null);
      } catch (error) {
        if (cancelled) {
          return;
        }

        setErrorMessage(error instanceof Error ? error.message : '차트 데이터를 불러오지 못했습니다.');
      } finally {
        if (!cancelled && showLoading) {
          setLoading(false);
        }
      }
    };

    void load();

    const intervalId = window.setInterval(() => {
      if (document.visibilityState !== 'visible') {
        return;
      }

      void load(false);
    }, CHART_REFRESH_INTERVAL_MS);

    const handleVisibilityChange = () => {
      if (document.visibilityState === 'visible') {
        void load(false);
      }
    };

    window.addEventListener('focus', handleVisibilityChange);
    document.addEventListener('visibilitychange', handleVisibilityChange);

    return () => {
      cancelled = true;
      window.clearInterval(intervalId);
      window.removeEventListener('focus', handleVisibilityChange);
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, []);

  const dailyPerformanceData = useMemo(() => buildDailyPerformanceData(items, DAILY_CHART_DAYS), [items]);
  const weeklyPerformanceData = useMemo(() => buildWeeklyPerformanceData(items, WEEKLY_CHART_WEEKS), [items]);
  const currentSnapshot = useMemo(() => buildCurrentPlanSnapshot(items), [items]);

  return (
    <div className="plan-board-page">
      <Card className="plan-board-page__overview" bordered={false}>
        <Title level={4}>Plan Charts</Title>
        <Paragraph className="plan-board-page__intro">
          현재 상태 집계와 최근 작업 등록, 기능동작 확인완료, 자동 작업 완료, main 반영 추이를 함께 확인합니다.
        </Paragraph>
      </Card>

      {errorMessage ? (
        <Card className="plan-board-page__chart-card" bordered={false}>
          <Empty description={errorMessage} />
        </Card>
      ) : (
        <Card
          title="작업성과 차트"
          className="plan-board-page__chart-card"
          loading={loading}
          bordered={false}
          extra={(
            <Space size={8}>
              <Text code>5초 자동갱신</Text>
              <Button size="small" onClick={() => void fetchLatestChartItems(setItems, setLoading, setErrorMessage)}>
                조회
              </Button>
            </Space>
          )}
        >
          <Flex vertical gap={16}>
            <CurrentSnapshotSummary snapshot={currentSnapshot} />

            <div className="plan-board-page__chart-grid">
              <PerformanceChart
                title="일별 작업성과"
                description={`최근 ${DAILY_CHART_DAYS}일 등록·기능확인완료·작업완료·main반영 건수`}
                data={dailyPerformanceData}
              />
              <PerformanceChart
                title="주별 작업성과"
                description={`최근 ${WEEKLY_CHART_WEEKS}주 등록·기능확인완료·작업완료·main반영 건수`}
                data={weeklyPerformanceData}
              />
            </div>
          </Flex>
        </Card>
      )}
    </div>
  );
}

async function fetchLatestChartItems(
  setItems: Dispatch<SetStateAction<PlanItem[]>>,
  setLoading: Dispatch<SetStateAction<boolean>>,
  setErrorMessage: Dispatch<SetStateAction<string | null>>,
) {
  setLoading(true);

  try {
    const nextItems = await fetchPlanItems('all');
    setItems(nextItems);
    setErrorMessage(null);
  } catch (error) {
    setErrorMessage(error instanceof Error ? error.message : '차트 데이터를 불러오지 못했습니다.');
  } finally {
    setLoading(false);
  }
}

function CurrentSnapshotSummary({ snapshot }: { snapshot: CurrentPlanSnapshot }) {
  return (
    <Flex justify="space-between" align="start" gap={12} wrap>
      <div>
        <Title level={5} className="plan-board-page__chart-title">
          현재데이터
        </Title>
        <Paragraph className="plan-board-page__chart-description">
          지금 저장된 작업 상태 기준 집계입니다. 완료 처리된 예전 작업도 상태가 바뀌면 바로 반영됩니다.
        </Paragraph>
      </div>
      <Space size={[8, 8]} wrap>
        <Tag>전체 {snapshot.total}</Tag>
        <Tag color="blue">등록 {snapshot.registered}</Tag>
        <Tag color="gold">작업중 {snapshot.inProgress}</Tag>
        <Tag color="purple">기능확인완료 {snapshot.functionChecked}</Tag>
        <Tag color="cyan">완료 {snapshot.completed}</Tag>
        <Tag color="green">main반영 {snapshot.merged}</Tag>
      </Space>
    </Flex>
  );
}

function PerformanceChart({
  title,
  description,
  data,
}: {
  title: string;
  description: string;
  data: PerformanceChartDatum[];
}) {
  const totalRegistered = data.reduce((sum, item) => sum + item.registered, 0);
  const totalJangsing = data.reduce((sum, item) => sum + item.jangsing, 0);
  const totalCompleted = data.reduce((sum, item) => sum + item.completed, 0);
  const totalMerged = data.reduce((sum, item) => sum + item.merged, 0);

  return (
    <div className="plan-board-page__chart-panel">
      <Flex justify="space-between" align="start" gap={12} wrap>
        <div>
          <Title level={5} className="plan-board-page__chart-title">
            {title}
          </Title>
          <Paragraph className="plan-board-page__chart-description">{description}</Paragraph>
        </div>
        <Space size={[8, 8]} wrap>
          <Tag color="blue">등록 {totalRegistered}</Tag>
          <Tag color="purple">기능확인완료 {totalJangsing}</Tag>
          <Tag color="cyan">완료 {totalCompleted}</Tag>
          <Tag color="green">main반영 {totalMerged}</Tag>
        </Space>
      </Flex>

      <div className="plan-board-page__chart-shell">
        <ResponsiveContainer width="100%" height={240}>
          <LineChart data={data} margin={{ top: 12, right: 12, left: -18, bottom: 0 }}>
            <CartesianGrid strokeDasharray="3 3" stroke="rgba(24, 34, 48, 0.08)" />
            <XAxis
              dataKey="label"
              tickLine={false}
              axisLine={false}
              tick={{ fill: 'rgba(24, 34, 48, 0.62)', fontSize: 12 }}
            />
            <YAxis
              allowDecimals={false}
              tickLine={false}
              axisLine={false}
              width={36}
              tick={{ fill: 'rgba(24, 34, 48, 0.62)', fontSize: 12 }}
            />
            <Tooltip
              contentStyle={{
                borderRadius: 12,
                border: '1px solid rgba(22, 93, 255, 0.12)',
                boxShadow: '0 16px 30px rgba(23, 61, 130, 0.08)',
              }}
            />
            <Legend />
            <Line
              type="monotone"
              dataKey="registered"
              name="등록"
              stroke="#165dff"
              strokeWidth={3}
              dot={{ r: 3, fill: '#165dff', stroke: '#ffffff', strokeWidth: 2 }}
              activeDot={{ r: 5 }}
            />
            <Line
              type="monotone"
              dataKey="jangsing"
              name="기능확인완료"
              stroke="#722ed1"
              strokeWidth={3}
              dot={{ r: 3, fill: '#722ed1', stroke: '#ffffff', strokeWidth: 2 }}
              activeDot={{ r: 5 }}
            />
            <Line
              type="monotone"
              dataKey="completed"
              name="작업완료"
              stroke="#13c2c2"
              strokeWidth={3}
              dot={{ r: 3, fill: '#13c2c2', stroke: '#ffffff', strokeWidth: 2 }}
              activeDot={{ r: 5 }}
            />
            <Line
              type="monotone"
              dataKey="merged"
              name="main반영"
              stroke="#52c41a"
              strokeWidth={3}
              dot={{ r: 3, fill: '#52c41a', stroke: '#ffffff', strokeWidth: 2 }}
              activeDot={{ r: 5 }}
            />
          </LineChart>
        </ResponsiveContainer>
      </div>
    </div>
  );
}

function buildDailyPerformanceData(items: PlanItem[], days: number): PerformanceChartDatum[] {
  const today = new Date();
  const start = startOfLocalDay(addDays(today, -(days - 1)));
  const keys = Array.from({ length: days }, (_, index) => formatLocalDateKey(addDays(start, index)));
  const buckets = createPerformanceBuckets(keys, (date) =>
    `${date.getMonth() + 1}.${String(date.getDate()).padStart(2, '0')}`,
  );

  items.forEach((item) => {
    accumulatePerformanceCount(buckets, item.createdAt, 'registered');
    if (item.jangsingProcessingRequired && item.completedAt) {
      accumulatePerformanceCount(buckets, item.completedAt, 'jangsing');
    }
    accumulatePerformanceCount(buckets, item.completedAt, 'completed');
    accumulatePerformanceCount(buckets, item.mergedAt, 'merged');
  });

  return keys.map((key) => buckets.get(key) ?? createEmptyPerformanceBucket(key, key));
}

function buildCurrentPlanSnapshot(items: PlanItem[]): CurrentPlanSnapshot {
  return items.reduce<CurrentPlanSnapshot>(
    (summary, item) => {
      summary.total += 1;

      if (item.status === '등록') {
        summary.registered += 1;
      }

      if (item.status === '작업중') {
        summary.inProgress += 1;
      }

      if (isCompletedPlanStatus(item.status)) {
        summary.completed += 1;

        if (item.jangsingProcessingRequired) {
          summary.functionChecked += 1;
        }
      }

      if (item.status === '완료' || item.mergedAt) {
        summary.merged += 1;
      }

      return summary;
    },
    {
      total: 0,
      registered: 0,
      inProgress: 0,
      functionChecked: 0,
      completed: 0,
      merged: 0,
    },
  );
}

function buildWeeklyPerformanceData(items: PlanItem[], weeks: number): PerformanceChartDatum[] {
  const today = new Date();
  const start = startOfLocalDay(addDays(today, -((weeks - 1) * 7)));
  const keys = Array.from({ length: weeks }, (_, index) => formatLocalDateKey(addDays(start, index * 7)));
  const buckets = createPerformanceBuckets(keys, (date) => {
    const weekEnd = addDays(date, 6);
    return `${date.getMonth() + 1}.${String(date.getDate()).padStart(2, '0')}-${weekEnd.getMonth() + 1}.${String(
      weekEnd.getDate(),
    ).padStart(2, '0')}`;
  });

  items.forEach((item) => {
    accumulateWeeklyPerformanceCount(buckets, item.createdAt, start, weeks, 'registered');
    if (item.jangsingProcessingRequired && item.completedAt) {
      accumulateWeeklyPerformanceCount(buckets, item.completedAt, start, weeks, 'jangsing');
    }
    accumulateWeeklyPerformanceCount(buckets, item.completedAt, start, weeks, 'completed');
    accumulateWeeklyPerformanceCount(buckets, item.mergedAt, start, weeks, 'merged');
  });

  return keys.map((key) => buckets.get(key) ?? createEmptyPerformanceBucket(key, key));
}

function createPerformanceBuckets(keys: string[], labelBuilder: (date: Date) => string) {
  const buckets = new Map<string, PerformanceChartDatum>();

  keys.forEach((key) => {
    const date = parseDateKey(key);
    buckets.set(key, createEmptyPerformanceBucket(key, labelBuilder(date)));
  });

  return buckets;
}

function createEmptyPerformanceBucket(key: string, label: string): PerformanceChartDatum {
  void key;

  return {
    label,
    registered: 0,
    jangsing: 0,
    completed: 0,
    merged: 0,
  };
}

function accumulatePerformanceCount(
  buckets: Map<string, PerformanceChartDatum>,
  value: string | null,
  field: keyof Omit<PerformanceChartDatum, 'label'>,
) {
  if (!value) {
    return;
  }

  const key = formatLocalDateKey(new Date(value));
  const bucket = buckets.get(key);

  if (bucket) {
    bucket[field] += 1;
  }
}

function accumulateWeeklyPerformanceCount(
  buckets: Map<string, PerformanceChartDatum>,
  value: string | null,
  start: Date,
  weeks: number,
  field: keyof Omit<PerformanceChartDatum, 'label'>,
) {
  if (!value) {
    return;
  }

  const date = startOfLocalDay(new Date(value));
  const diffDays = Math.floor((date.getTime() - start.getTime()) / (24 * 60 * 60 * 1000));

  if (diffDays < 0) {
    return;
  }

  const weekIndex = Math.floor(diffDays / 7);

  if (weekIndex < 0 || weekIndex >= weeks) {
    return;
  }

  const bucketKey = formatLocalDateKey(addDays(start, weekIndex * 7));
  const bucket = buckets.get(bucketKey);

  if (bucket) {
    bucket[field] += 1;
  }
}

function startOfLocalDay(date: Date) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}

function addDays(date: Date, days: number) {
  const next = new Date(date);
  next.setDate(next.getDate() + days);
  return next;
}

function formatLocalDateKey(date: Date) {
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(
    2,
    '0',
  )}`;
}

function parseDateKey(key: string) {
  const [year, month, day] = key.split('-').map(Number);
  return new Date(year, (month ?? 1) - 1, day ?? 1);
}

function isCompletedPlanStatus(status: PlanItem['status']) {
  return status === '작업완료' || status === '릴리즈완료' || status === '완료';
}
