import { CheckOutlined, CloseOutlined, DeleteOutlined, DownOutlined, ReloadOutlined, SaveOutlined, SearchOutlined, UpOutlined } from '@ant-design/icons';
import { Button, Input, Popconfirm, Select, Spin, Tooltip, message } from 'antd';
import type { BodyScrollEndEvent, ColDef, GridReadyEvent, RowDoubleClickedEvent, ValueFormatterParams } from 'ag-grid-community';
import { AllCommunityModule, ModuleRegistry } from 'ag-grid-community';
import { AgGridReact } from 'ag-grid-react';
import { useEffect, useRef, useState } from 'react';
import {
  deleteTestAppMaintenanceRequest,
  fetchTestAppMaintenanceRequests,
  saveTestAppMaintenanceRequests,
  type TestAppMaintenanceRequestFilters,
  type TestAppMaintenanceRequestRow,
} from './testPlayAppApi';
import './TestPlayAppView.css';

import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';

ModuleRegistry.registerModules([AllCommunityModule]);

const PAGE_SIZE = 100;
const STATUS_OPTIONS = ['전체', '접수', '배정완료', '조치중', '부품대기', '완료'];
const PRIORITY_OPTIONS = ['전체', '긴급', '높음', '보통', '낮음'];
const LINE_OPTIONS = ['전체', 'PKG', 'MFG', 'UTL', 'QC'];
const EDITABLE_PRIORITY_OPTIONS: TestAppMaintenanceRequestRow['priority'][] = ['긴급', '높음', '보통', '낮음'];
const EDITABLE_STATUS_OPTIONS: TestAppMaintenanceRequestRow['status'][] = ['접수', '배정완료', '조치중', '부품대기', '완료'];
const EDITABLE_FIELDS = ['priority', 'status', 'assigneeName'] as const;

type MaintenancePriority = TestAppMaintenanceRequestRow['priority'];
type MaintenanceFilterState = TestAppMaintenanceRequestFilters;
type MaintenanceRequestRow = TestAppMaintenanceRequestRow;
type EditableField = (typeof EDITABLE_FIELDS)[number];
type EditedFieldEntry = Partial<Record<EditableField, true>>;
type EditedFieldMap = Record<number, EditedFieldEntry>;

const INITIAL_FILTERS: MaintenanceFilterState = {
  keyword: '',
  lineCode: '전체',
  priority: '전체',
  status: '전체',
  requestedFrom: '',
  requestedTo: '',
};

function priorityRank(priority: MaintenancePriority) {
  if (priority === '긴급') {
    return 4;
  }
  if (priority === '높음') {
    return 3;
  }
  if (priority === '보통') {
    return 2;
  }
  return 1;
}

function formatDateTime(params: ValueFormatterParams<MaintenanceRequestRow>) {
  return params.value ? String(params.value).replace('T', ' ').slice(0, 16) : '-';
}

function resolveEditedFieldEntry(row: MaintenanceRequestRow, baseline?: MaintenanceRequestRow) {
  const editedEntry: EditedFieldEntry = {};

  if (!baseline) {
    return editedEntry;
  }

  for (const field of EDITABLE_FIELDS) {
    if (row[field] !== baseline[field]) {
      editedEntry[field] = true;
    }
  }

  return editedEntry;
}

export function TestPlayAppView() {
  const [messageApi, contextHolder] = message.useMessage();
  const gridApiRef = useRef<GridReadyEvent<MaintenanceRequestRow>['api'] | null>(null);
  const baselineRowsRef = useRef<Record<number, MaintenanceRequestRow>>({});
  const [filters, setFilters] = useState<MaintenanceFilterState>(INITIAL_FILTERS);
  const [draftFilters, setDraftFilters] = useState<MaintenanceFilterState>(INITIAL_FILTERS);
  const [rows, setRows] = useState<MaintenanceRequestRow[]>([]);
  const [isExpanded, setIsExpanded] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [isSaving, setIsSaving] = useState(false);
  const [isDeleting, setIsDeleting] = useState(false);
  const [page, setPage] = useState(1);
  const [total, setTotal] = useState(0);
  const [hasNext, setHasNext] = useState(false);
  const [editedFieldMap, setEditedFieldMap] = useState<EditedFieldMap>({});
  const [editingRowId, setEditingRowId] = useState<number | null>(null);
  const loadingNextPageRef = useRef(false);

  const editingRow = editingRowId === null ? null : rows.find((row) => row.id === editingRowId) ?? null;
  const loadedEnd = rows.length;
  const loadedRangeLabel = loadedEnd > 0 ? `1-${loadedEnd.toLocaleString()} / ${total.toLocaleString()}` : `0 / ${total.toLocaleString()}`;
  const pageLabel = `${page}p`;

  const updateRowDraft = (rowId: number, patch: Partial<Pick<MaintenanceRequestRow, EditableField>>) => {
    const currentRow = rows.find((row) => row.id === rowId);

    if (!currentRow) {
      return;
    }

    const nextRow = {
      ...currentRow,
      ...patch,
    };
    const editedEntry = resolveEditedFieldEntry(nextRow, baselineRowsRef.current[rowId]);
    const isDirty = Object.keys(editedEntry).length > 0;

    setRows((previousRows) =>
      previousRows.map((row) =>
        row.id === rowId
          ? {
              ...nextRow,
              isDirty,
            }
          : row,
      ),
    );
    setEditedFieldMap((previousMap) => {
      const nextMap = { ...previousMap };

      if (isDirty) {
        nextMap[rowId] = editedEntry;
      } else {
        delete nextMap[rowId];
      }

      return nextMap;
    });
  };

  const loadRows = async (nextPage: number, nextFilters: MaintenanceFilterState, append = false) => {
    setIsLoading(true);

    if (!append) {
      setEditingRowId(null);
    }

    try {
      const response = await fetchTestAppMaintenanceRequests(nextPage, PAGE_SIZE, nextFilters);
      const nextRows = response.items.map((item) => ({ ...item, isDirty: false }));

      if (append) {
        for (const row of nextRows) {
          baselineRowsRef.current[row.id] = { ...row, isDirty: false };
        }
      } else {
        baselineRowsRef.current = Object.fromEntries(nextRows.map((row) => [row.id, { ...row, isDirty: false }]));
        setEditedFieldMap({});
      }

      setRows((previousRows) => (append ? [...previousRows, ...nextRows] : nextRows));
      setTotal(response.total);
      setPage(response.page);
      setHasNext(response.hasNext);
    } catch (error) {
      messageApi.error(error instanceof Error ? error.message : '점검 요청 데이터를 불러오지 못했습니다.');
    } finally {
      setIsLoading(false);
      loadingNextPageRef.current = false;
    }
  };

  useEffect(() => {
    void loadRows(1, INITIAL_FILTERS);
  }, []);

  useEffect(() => {
    if (editingRowId === null) {
      return;
    }

    if (!editingRow) {
      setEditingRowId(null);
      return;
    }

    const html = document.documentElement;
    const body = document.body;
    const previousHtmlOverflow = html.style.overflow;
    const previousBodyOverflow = body.style.overflow;
    const previousBodyPaddingRight = body.style.paddingRight;
    const scrollbarWidth = Math.max(0, window.innerWidth - html.clientWidth);

    html.style.overflow = 'hidden';
    body.style.overflow = 'hidden';

    if (scrollbarWidth > 0) {
      body.style.paddingRight = `${scrollbarWidth}px`;
    }

    return () => {
      html.style.overflow = previousHtmlOverflow;
      body.style.overflow = previousBodyOverflow;
      body.style.paddingRight = previousBodyPaddingRight;
    };
  }, [editingRow, editingRowId]);

  useEffect(() => {
    if (editingRowId === null) {
      return;
    }

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        setEditingRowId(null);
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => {
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [editingRowId]);

  const handleSearch = () => {
    const nextFilters = {
      ...draftFilters,
      keyword: draftFilters.keyword.trim(),
    };

    setFilters(nextFilters);
    void loadRows(1, nextFilters);
  };

  const handleReset = () => {
    setDraftFilters(INITIAL_FILTERS);
    setFilters(INITIAL_FILTERS);
    void loadRows(1, INITIAL_FILTERS);
  };

  const handleSave = async () => {
    const dirtyRows = rows.filter((row) => row.isDirty);

    if (!dirtyRows.length) {
      messageApi.info('저장할 변경 행이 없습니다.');
      return;
    }

    setIsSaving(true);

    try {
      await saveTestAppMaintenanceRequests(dirtyRows);
      await loadRows(1, filters);
      messageApi.success('저장을 완료했습니다.');
    } catch (error) {
      messageApi.error(error instanceof Error ? error.message : '저장에 실패했습니다.');
    } finally {
      setIsSaving(false);
    }
  };

  const handleDelete = async () => {
    if (!editingRow) {
      return;
    }

    setIsDeleting(true);

    try {
      await deleteTestAppMaintenanceRequest(editingRow.id);
      setEditingRowId(null);
      await loadRows(1, filters);
      messageApi.success('요청을 삭제했습니다.');
    } catch (error) {
      messageApi.error(error instanceof Error ? error.message : '삭제에 실패했습니다.');
    } finally {
      setIsDeleting(false);
    }
  };

  const handleBodyScrollEnd = (event: BodyScrollEndEvent<MaintenanceRequestRow>) => {
    if (event.direction !== 'vertical' || loadingNextPageRef.current || isLoading || !hasNext) {
      return;
    }

    const api = gridApiRef.current;

    if (!api) {
      return;
    }

    const range = api.getVerticalPixelRange();
    const container = document.querySelector('.test-play-app__grid-surface .ag-body-viewport') as HTMLElement | null;
    const remaining = container ? container.scrollHeight - range.bottom : Number.MAX_SAFE_INTEGER;

    if (remaining > 80) {
      return;
    }

    loadingNextPageRef.current = true;
    void loadRows(page + 1, filters, true);
  };

  const columnDefs: ColDef<MaintenanceRequestRow>[] = [
    { field: 'requestNo', headerName: '작업번호', minWidth: 132, maxWidth: 148, pinned: 'left' },
    { field: 'lineCode', headerName: '라인', minWidth: 110, maxWidth: 124 },
    { field: 'equipmentName', headerName: '설비명', minWidth: 190, flex: 1.2 },
    { field: 'issueType', headerName: '이상유형', minWidth: 144, flex: 1 },
    {
      field: 'priority',
      headerName: '우선순위',
      minWidth: 108,
      maxWidth: 118,
      comparator: (left: MaintenancePriority, right: MaintenancePriority) => priorityRank(left) - priorityRank(right),
    },
    { field: 'requesterName', headerName: '요청자', minWidth: 112, maxWidth: 128 },
    { field: 'assigneeName', headerName: '담당자', minWidth: 112, maxWidth: 128 },
    { field: 'status', headerName: '상태', minWidth: 120, maxWidth: 132 },
    { field: 'requestedAt', headerName: '요청시각', minWidth: 168, valueFormatter: formatDateTime },
    { field: 'lastActionAt', headerName: '최근조치시각', minWidth: 176, valueFormatter: formatDateTime },
  ];

  for (const column of columnDefs) {
    column.cellClass = (params) => {
      const classNames = ['test-play-app__grid-cell'];
      const rowId = params.data?.id ?? null;
      const fieldName = params.colDef.field as EditableField | undefined;

      if (rowId !== null && fieldName && editedFieldMap[rowId]?.[fieldName]) {
        classNames.push('test-play-app__grid-cell--edited');
      }

      if (rowId !== null && editingRowId === rowId) {
        classNames.push('test-play-app__grid-cell--active');
      }

      return classNames;
    };
  }

  return (
    <>
      {contextHolder}
      <div className="test-play-app">
        <section className="test-play-app__filters">
          <div className="test-play-app__heading">
            <strong>설비 점검 요청 목록</strong>
          </div>

          <div className="test-play-app__filters-main">
            <Input
              className="test-play-app__search-input"
              value={draftFilters.keyword}
              placeholder="설비명 · 작업번호 · 요청자 검색"
              onChange={(event) => {
                setDraftFilters((previous) => ({
                  ...previous,
                  keyword: event.target.value,
                }));
              }}
              aria-label="검색어"
              onPressEnter={handleSearch}
            />

            <div className="test-play-app__icon-actions">
              <Tooltip title="조회">
                <Button className="test-play-app__icon-button test-play-app__icon-button--primary" aria-label="조회" icon={<SearchOutlined />} type="primary" onClick={handleSearch} />
              </Tooltip>
              <Tooltip title={isExpanded ? '추가 필터 접기' : '추가 필터 펼치기'}>
                <Button
                  type="default"
                  className="test-play-app__icon-button test-play-app__filters-toggle"
                  aria-label={isExpanded ? '추가 필터 접기' : '추가 필터 펼치기'}
                  aria-expanded={isExpanded}
                  icon={isExpanded ? <UpOutlined /> : <DownOutlined />}
                  onClick={() => {
                    setIsExpanded((previous) => !previous);
                  }}
                />
              </Tooltip>
            </div>
          </div>

          <div className={`test-play-app__filters-detail ${isExpanded ? 'test-play-app__filters-detail--expanded' : ''}`}>
            <div className="test-play-app__filters-extra-shell">
              <div className="test-play-app__filters-extra">
                <label className="test-play-app__filter-field">
                  <span>라인</span>
                  <Select
                    value={draftFilters.lineCode}
                    options={LINE_OPTIONS.map((value) => ({ value, label: value }))}
                    onChange={(value) => {
                      setDraftFilters((previous) => ({
                        ...previous,
                        lineCode: value,
                      }));
                    }}
                  />
                </label>
                <label className="test-play-app__filter-field">
                  <span>우선순위</span>
                  <Select
                    value={draftFilters.priority}
                    options={PRIORITY_OPTIONS.map((value) => ({ value, label: value }))}
                    onChange={(value) => {
                      setDraftFilters((previous) => ({
                        ...previous,
                        priority: value,
                      }));
                    }}
                  />
                </label>
                <label className="test-play-app__filter-field">
                  <span>상태</span>
                  <Select
                    value={draftFilters.status}
                    options={STATUS_OPTIONS.map((value) => ({ value, label: value }))}
                    onChange={(value) => {
                      setDraftFilters((previous) => ({
                        ...previous,
                        status: value,
                      }));
                    }}
                  />
                </label>
                <label className="test-play-app__filter-field">
                  <span>요청 시작일</span>
                  <Input
                    type="date"
                    value={draftFilters.requestedFrom}
                    onChange={(event) => {
                      setDraftFilters((previous) => ({
                        ...previous,
                        requestedFrom: event.target.value,
                      }));
                    }}
                  />
                </label>
                <label className="test-play-app__filter-field">
                  <span>요청 종료일</span>
                  <Input
                    type="date"
                    value={draftFilters.requestedTo}
                    onChange={(event) => {
                      setDraftFilters((previous) => ({
                        ...previous,
                        requestedTo: event.target.value,
                      }));
                    }}
                  />
                </label>
              </div>
            </div>
          </div>
        </section>

        <section className="test-play-app__grid-panel">
          <div className="test-play-app__grid-toolbar">
            <div className="test-play-app__grid-meta-inline" aria-label="페이지 정보">
              <span className="test-play-app__grid-meta-inline-range">{loadedRangeLabel}</span>
              <span className="test-play-app__grid-meta-inline-divider" aria-hidden="true">
                ·
              </span>
              <span className="test-play-app__grid-meta-inline-page">{pageLabel}</span>
            </div>
            <div className="test-play-app__grid-actions">
              <Tooltip title="검색조건 초기화">
                <Button className="test-play-app__icon-button" aria-label="검색조건 초기화" icon={<ReloadOutlined />} onClick={handleReset} />
              </Tooltip>
              <Tooltip title="저장">
                <Button
                  className="test-play-app__icon-button test-play-app__icon-button--primary"
                  aria-label="저장"
                  icon={<SaveOutlined />}
                  type="primary"
                  loading={isSaving}
                  onClick={() => void handleSave()}
                />
              </Tooltip>
            </div>
          </div>

          <div className="test-play-app__grid-surface ag-theme-quartz">
            <Spin spinning={isLoading}>
              <AgGridReact<MaintenanceRequestRow>
                rowData={rows}
                columnDefs={columnDefs}
                defaultColDef={{
                  sortable: true,
                  resizable: true,
                  editable: false,
                  filter: false,
                }}
                getRowId={(params) => String(params.data.id)}
                rowHeight={42}
                headerHeight={42}
                suppressMovableColumns
                animateRows={false}
                onGridReady={(event) => {
                  gridApiRef.current = event.api;
                  event.api.sizeColumnsToFit();
                }}
                onGridSizeChanged={() => {
                  gridApiRef.current?.sizeColumnsToFit();
                }}
                onBodyScrollEnd={handleBodyScrollEnd}
                onRowDoubleClicked={(event: RowDoubleClickedEvent<MaintenanceRequestRow>) => {
                  if (!event.data) {
                    return;
                  }

                  setEditingRowId(event.data.id);
                }}
                rowClassRules={{
                  'test-play-app__grid-row--odd': (params) => ((params.node.rowIndex ?? 0) + 1) % 2 === 1,
                  'test-play-app__grid-row--even': (params) => ((params.node.rowIndex ?? 0) + 1) % 2 === 0,
                  'test-play-app__grid-row--dirty': (params) => Boolean(params.data?.isDirty),
                  'test-play-app__grid-row--active': (params) => params.data?.id === editingRowId,
                }}
              />
            </Spin>
          </div>
        </section>
      </div>

      <div className={`test-play-app__editor-shell ${editingRow ? 'test-play-app__editor-shell--open' : ''}`} aria-hidden={editingRow ? 'false' : 'true'}>
        <button
          type="button"
          className="test-play-app__editor-backdrop"
          aria-label="편집 패널 닫기"
          onClick={() => {
            setEditingRowId(null);
          }}
        />

        <section className="test-play-app__editor-panel" aria-label="점검 요청 편집 패널">
          <header className="test-play-app__editor-header">
            <div className="test-play-app__editor-heading">
              <strong>{editingRow?.equipmentName ?? '점검 요청 편집'}</strong>
              <span>{editingRow ? `${editingRow.requestNo} · ${editingRow.lineCode} · ${editingRow.issueType}` : '행 더블클릭으로 편집 패널을 엽니다.'}</span>
            </div>

            <div className="test-play-app__editor-actions">
              <Popconfirm
                title="현재 요청을 삭제할까요?"
                description="삭제 후에는 목록에서 제거되고 새로고침 후에도 복구되지 않습니다."
                okText="삭제"
                cancelText="취소"
                okButtonProps={{ danger: true, loading: isDeleting }}
                onConfirm={() => void handleDelete()}
                disabled={!editingRow}
              >
                <Tooltip title="삭제">
                  <Button
                    danger
                    type="default"
                    className="test-play-app__icon-button test-play-app__icon-button--danger test-play-app__editor-delete"
                    aria-label="삭제"
                    icon={<DeleteOutlined />}
                    loading={isDeleting}
                    disabled={!editingRow}
                  />
                </Tooltip>
              </Popconfirm>
              <Tooltip title="적용 후 닫기">
                <Button
                  className="test-play-app__icon-button test-play-app__icon-button--primary test-play-app__editor-apply"
                  aria-label="적용 후 닫기"
                  icon={<CheckOutlined />}
                  type="primary"
                  disabled={!editingRow}
                  onClick={() => {
                    if (!editingRow) {
                      return;
                    }

                    setEditingRowId(null);
                  }}
                />
              </Tooltip>
              <Tooltip title="닫기">
                <Button
                  type="default"
                  className="test-play-app__icon-button test-play-app__editor-close"
                  aria-label="닫기"
                  icon={<CloseOutlined />}
                  onClick={() => {
                    setEditingRowId(null);
                  }}
                />
              </Tooltip>
            </div>
          </header>

          <div className="test-play-app__editor-body">
            <section className="test-play-app__editor-summary">
              <div className="test-play-app__editor-card">
                <span className="test-play-app__editor-card-label">작업번호</span>
                <strong>{editingRow?.requestNo ?? '-'}</strong>
              </div>
              <div className="test-play-app__editor-card">
                <span className="test-play-app__editor-card-label">요청자</span>
                <strong>{editingRow?.requesterName ?? '-'}</strong>
              </div>
              <div className="test-play-app__editor-card">
                <span className="test-play-app__editor-card-label">요청시각</span>
                <strong>{editingRow?.requestedAt ? editingRow.requestedAt.replace('T', ' ').slice(0, 16) : '-'}</strong>
              </div>
              <div className="test-play-app__editor-card">
                <span className="test-play-app__editor-card-label">최근조치</span>
                <strong>{editingRow?.lastActionAt ? editingRow.lastActionAt.replace('T', ' ').slice(0, 16) : '-'}</strong>
              </div>
            </section>

            <section className="test-play-app__editor-form">
              <div className="test-play-app__editor-form-grid">
                <label className="test-play-app__editor-field">
                  <span>설비명</span>
                  <Input value={editingRow?.equipmentName ?? ''} readOnly />
                </label>
                <label className="test-play-app__editor-field">
                  <span>이상유형</span>
                  <Input value={editingRow?.issueType ?? ''} readOnly />
                </label>
                <label className={`test-play-app__editor-field ${editingRow && editedFieldMap[editingRow.id]?.priority ? 'test-play-app__editor-field--edited' : ''}`}>
                  <span>우선순위</span>
                  <Select
                    value={editingRow?.priority}
                    options={EDITABLE_PRIORITY_OPTIONS.map((value) => ({ value, label: value }))}
                    disabled={!editingRow}
                    onChange={(value) => {
                      if (!editingRow) {
                        return;
                      }

                      updateRowDraft(editingRow.id, { priority: value });
                    }}
                  />
                </label>
                <label className={`test-play-app__editor-field ${editingRow && editedFieldMap[editingRow.id]?.status ? 'test-play-app__editor-field--edited' : ''}`}>
                  <span>상태</span>
                  <Select
                    value={editingRow?.status}
                    options={EDITABLE_STATUS_OPTIONS.map((value) => ({ value, label: value }))}
                    disabled={!editingRow}
                    onChange={(value) => {
                      if (!editingRow) {
                        return;
                      }

                      updateRowDraft(editingRow.id, { status: value });
                    }}
                  />
                </label>
                <label className={`test-play-app__editor-field ${editingRow && editedFieldMap[editingRow.id]?.assigneeName ? 'test-play-app__editor-field--edited' : ''}`}>
                  <span>담당자</span>
                  <Input
                    value={editingRow?.assigneeName ?? ''}
                    placeholder="담당자를 입력하세요."
                    disabled={!editingRow}
                    onChange={(event) => {
                      if (!editingRow) {
                        return;
                      }

                      updateRowDraft(editingRow.id, { assigneeName: event.target.value });
                    }}
                  />
                </label>
                <div className="test-play-app__editor-note">
                  <strong>저장 범위</strong>
                  <span>현재 패널에서는 우선순위, 상태, 담당자만 수정합니다. 변경 사항은 하단 저장 버튼으로 일괄 반영됩니다.</span>
                </div>
              </div>
            </section>
          </div>
        </section>
      </div>
    </>
  );
}
