import { DeleteOutlined, PlusOutlined, ReloadOutlined, SaveOutlined } from '@ant-design/icons';
import { Button, Space, message } from 'antd';
import type { ColDef, GridApi, GridReadyEvent } from 'ag-grid-community';
import { AllCommunityModule, ModuleRegistry } from 'ag-grid-community';
import { AgGridReact } from 'ag-grid-react';
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { WidgetShell } from '../core';
import type { WidgetHandle } from '../core';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';
import './AgGridWidget.css';

ModuleRegistry.registerModules([AllCommunityModule]);

const STORAGE_KEY = 'ai-code-app:ag-grid-widget';

type GridRow = {
  id: string;
  item: string;
  status: string;
  owner: string;
  quantity: number;
  [key: string]: string | number;
};

type StoredGridSnapshot = {
  columnDefs: ColDef<GridRow>[];
  rowData: GridRow[];
};

const BASE_COLUMNS: ColDef<GridRow>[] = [
  { field: 'item', headerName: '품목', editable: true, minWidth: 150, flex: 1.2 },
  { field: 'status', headerName: '상태', editable: true, minWidth: 120, flex: 1 },
  { field: 'owner', headerName: '담당자', editable: true, minWidth: 120, flex: 1 },
  { field: 'quantity', headerName: '수량', editable: true, minWidth: 100, type: 'numericColumn', flex: 0.8 },
];

const DEFAULT_ROWS: GridRow[] = [
  { id: '1', item: '모바일 레이아웃 QA', status: '대기', owner: '민지', quantity: 3 },
  { id: '2', item: '채팅방 최신글 확인', status: '진행', owner: '도윤', quantity: 5 },
  { id: '3', item: '첨부파일 미리보기', status: '검토', owner: '서준', quantity: 2 },
  { id: '4', item: '위젯 저장 상태 점검', status: '완료', owner: '하린', quantity: 8 },
  { id: '5', item: '사용자 액션 로그', status: '대기', owner: '지후', quantity: 1 },
];

export type AgGridWidgetProps = {
  title?: string;
  cardWrapper?: boolean;
  height?: number | string;
};

function normalizeHeight(height?: number | string) {
  if (typeof height === 'number') {
    return `${height}px`;
  }

  return height ?? '100%';
}

function cloneColumnDefs(columnDefs: ColDef<GridRow>[]) {
  return columnDefs.map((columnDef) => ({ ...columnDef }));
}

function cloneRowData(rowData: GridRow[]) {
  return rowData.map((row) => ({ ...row }));
}

function readStoredSnapshot() {
  if (typeof window === 'undefined') {
    return null;
  }

  try {
    const rawValue = window.localStorage.getItem(STORAGE_KEY);
    if (!rawValue) {
      return null;
    }

    const parsed = JSON.parse(rawValue) as StoredGridSnapshot;
    if (!Array.isArray(parsed?.columnDefs) || !Array.isArray(parsed?.rowData)) {
      return null;
    }

    return {
      columnDefs: cloneColumnDefs(parsed.columnDefs),
      rowData: cloneRowData(parsed.rowData),
    } satisfies StoredGridSnapshot;
  } catch {
    return null;
  }
}

function getDefaultSnapshot(): StoredGridSnapshot {
  return {
    columnDefs: cloneColumnDefs(BASE_COLUMNS),
    rowData: cloneRowData(DEFAULT_ROWS),
  };
}

function getCurrentRows(api: GridApi<GridRow> | null, fallbackRows: GridRow[]) {
  if (!api) {
    return cloneRowData(fallbackRows);
  }

  const rows: GridRow[] = [];
  api.forEachNode((node) => {
    if (node.data) {
      rows.push({ ...node.data });
    }
  });
  return rows;
}

function getNextExtraColumnIndex(columnDefs: ColDef<GridRow>[]) {
  return (
    columnDefs.reduce((maxValue, columnDef) => {
      if (typeof columnDef.field !== 'string') {
        return maxValue;
      }

      const match = columnDef.field.match(/^extraColumn(\d+)$/);
      if (!match) {
        return maxValue;
      }

      return Math.max(maxValue, Number(match[1]));
    }, 0) + 1
  );
}

export const AgGridWidget = forwardRef<WidgetHandle, AgGridWidgetProps>(function AgGridWidget(
  { title = 'AG Grid Widget', cardWrapper = true, height },
  ref,
) {
  const [messageApi, contextHolder] = message.useMessage();
  const wrapperRef = useRef<HTMLDivElement | null>(null);
  const gridApiRef = useRef<GridApi<GridRow> | null>(null);
  const initialSnapshot = useMemo(() => readStoredSnapshot() ?? getDefaultSnapshot(), []);
  const [columnDefs, setColumnDefs] = useState<ColDef<GridRow>[]>(initialSnapshot.columnDefs);
  const [rowData, setRowData] = useState<GridRow[]>(initialSnapshot.rowData);

  const defaultColDef = useMemo<ColDef<GridRow>>(
    () => ({
      sortable: true,
      filter: true,
      resizable: true,
      editable: true,
    }),
    [],
  );

  const resizeColumnsToFit = () => {
    window.requestAnimationFrame(() => {
      gridApiRef.current?.sizeColumnsToFit({
        defaultMinWidth: 96,
      });
    });
  };

  useImperativeHandle(
    ref,
    () => ({
      focus: () => {
        wrapperRef.current?.focus();
      },
      scrollIntoView: (options) => {
        wrapperRef.current?.scrollIntoView(options);
      },
      getId: () => 'ag-grid-widget',
      getFeatures: () => ['component-sample', 'feature-registry', 'imperative-handle'],
    }),
    [],
  );

  useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    const target = wrapperRef.current;
    if (!target || typeof ResizeObserver === 'undefined') {
      resizeColumnsToFit();
      return;
    }

    const observer = new ResizeObserver(() => {
      resizeColumnsToFit();
    });
    observer.observe(target);
    return () => observer.disconnect();
  }, [columnDefs.length]);

  useEffect(() => {
    resizeColumnsToFit();
  }, [columnDefs]);

  const applySnapshot = (snapshot: StoredGridSnapshot) => {
    setColumnDefs(cloneColumnDefs(snapshot.columnDefs));
    setRowData(cloneRowData(snapshot.rowData));
    window.requestAnimationFrame(() => {
      gridApiRef.current?.refreshCells({ force: true });
      resizeColumnsToFit();
    });
  };

  const handleGridReady = (event: GridReadyEvent<GridRow>) => {
    gridApiRef.current = event.api;
    resizeColumnsToFit();
  };

  const handleAddColumn = () => {
    const nextIndex = getNextExtraColumnIndex(columnDefs);
    const nextField = `extraColumn${nextIndex}`;
    const nextRows = getCurrentRows(gridApiRef.current, rowData).map((row) => ({
      ...row,
      [nextField]: '',
    }));

    setColumnDefs((previous) => [
      ...previous,
      {
        field: nextField,
        headerName: `열 ${nextIndex}`,
        editable: true,
        minWidth: 120,
        flex: 1,
      },
    ]);
    setRowData(nextRows);
  };

  const handleRemoveColumn = () => {
    const removableColumn = [...columnDefs].reverse().find((columnDef) => {
      return typeof columnDef.field === 'string' && !BASE_COLUMNS.some((baseColumn) => baseColumn.field === columnDef.field);
    });

    if (!removableColumn?.field) {
      void messageApi.info('삭제할 추가 열이 없습니다.');
      return;
    }

    const nextRows = getCurrentRows(gridApiRef.current, rowData).map((row) => {
      const nextRow = { ...row };
      delete nextRow[removableColumn.field as string];
      return nextRow;
    });

    setColumnDefs((previous) => previous.filter((columnDef) => columnDef.field !== removableColumn.field));
    setRowData(nextRows);
  };

  const handleSave = () => {
    if (typeof window === 'undefined') {
      return;
    }

    const snapshot: StoredGridSnapshot = {
      columnDefs: cloneColumnDefs(columnDefs),
      rowData: getCurrentRows(gridApiRef.current, rowData),
    };

    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot));
    void messageApi.success('그리드를 저장했습니다.');
  };

  const handleRefresh = () => {
    const savedSnapshot = readStoredSnapshot();

    if (savedSnapshot) {
      applySnapshot(savedSnapshot);
      void messageApi.success('저장된 그리드를 다시 불러왔습니다.');
      return;
    }

    applySnapshot(getDefaultSnapshot());
    void messageApi.info('기본 그리드로 새로고침했습니다.');
  };

  return (
    <div
      ref={wrapperRef}
      className="ag-grid-widget-frame"
      style={{ height: normalizeHeight(height) }}
      tabIndex={-1}
    >
      {contextHolder}
      <WidgetShell
        id="ag-grid-widget"
        title={title}
        cardWrapper={cardWrapper}
        featureSlot={
          <Space size={8} wrap className="ag-grid-widget__actions">
            <Button
              aria-label="열 추가"
              title="열 추가"
              icon={<PlusOutlined />}
              className="ag-grid-widget__icon-button"
              onClick={handleAddColumn}
            />
            <Button
              aria-label="열 삭제"
              title="열 삭제"
              icon={<DeleteOutlined />}
              className="ag-grid-widget__icon-button"
              onClick={handleRemoveColumn}
            />
            <Button
              aria-label="저장"
              title="저장"
              icon={<SaveOutlined />}
              className="ag-grid-widget__icon-button"
              type="primary"
              onClick={handleSave}
            />
            <Button
              aria-label="새로고침"
              title="새로고침"
              icon={<ReloadOutlined />}
              className="ag-grid-widget__icon-button"
              onClick={handleRefresh}
            />
          </Space>
        }
      >
        <div className="ag-grid-widget">
          <div className="ag-grid-widget__grid-shell ag-theme-quartz">
            <AgGridReact<GridRow>
              animateRows
              columnDefs={columnDefs}
              rowData={rowData}
              defaultColDef={defaultColDef}
              getRowId={(params) => params.data.id}
              onGridReady={handleGridReady}
            />
          </div>
        </div>
      </WidgetShell>
    </div>
  );
});
