// aislop-ignore-file: duplicate-block -- 表格/表单声明结构相似且参数不同，渲染逻辑已共享组件化
import React, { useState, useMemo, useCallback } from 'react';
import { useNavigate } from 'react-router';
import { Alert, App, Form } from 'antd';
import dayjs, { type Dayjs } from 'dayjs';
import api from '../../api';
import { message } from '../../ui/app-message';
import { buildCheckInPayload, buildTransferPayload } from './occupancy-form';
import { buildOccupancyColumns } from './OccupancyColumns';
import type { OccupancyRow } from './OccupancyColumns';
import {
  BatchCheckOutModal,
  CheckInModal,
  CheckOutModal,
  TransferModal,
} from './OccupancyModals';
import { usePermission } from '../../hooks/usePermission';
import { occupancyParamsForView, occupancyViewPolicy, type OccupancyView } from '../archive-view';
import { useQuery } from '@tanstack/react-query';
import { validateResponse } from '../../utils/validate';
import { occupanciesSchema } from '../../api/schemas';
import { OccupanciesTableArea } from './OccupanciesTableArea';
import { OccupanciesToolbar } from './OccupanciesToolbar';
import { useOccupancyMutations } from './useOccupancyMutations';
import { QueryErrorState } from '../../components/QueryState';
import { NextStepHint } from '../../components/NextStepHint';
import { useVisibleRefetch } from '../../hooks/usePageVisible';

interface StudentLookupRow {
  id: number;
  name: string;
  studentNo?: string;
  idNumber?: string;
  phone?: string;
  status?: string;
}

interface RoomOverviewRow {
  id: number;
  roomNumber: string;
  building?: string;
  capacity?: number;
  currentCount?: number;
  floor?: number | null;
  roomType?: string;
  status?: string;
}

const OccupanciesPage: React.FC = () => {
  const { modal } = App.useApp();
  const navigate = useNavigate();
  const { hasPermission, permissionsReady } = usePermission();
  const canCheckIn = permissionsReady && hasPermission('occupancy:checkin');
  const canCheckOut = permissionsReady && hasPermission('occupancy:checkout');
  const canTransfer = permissionsReady && hasPermission('occupancy:transfer');
  const canDelete = permissionsReady && hasPermission('occupancy:delete');
  const canPurge = permissionsReady && hasPermission('occupancy:purge');
  const [checkInModal, setCheckInModal] = useState(false);
  const [checkOutModal, setCheckOutModal] = useState<any>(null);
  const [transferModal, setTransferModal] = useState<any>(null);
  // 入住成功后的「下一步」引导提示
  const [nextStepHint, setNextStepHint] = useState<'billing' | null>(null);
  const [viewMode, setViewMode] = useState<OccupancyView>('active');
  const viewPolicy = occupancyViewPolicy(viewMode);
  const [autoDeposit, setAutoDeposit] = useState(true);
  const [depositAmount, setDepositAmount] = useState(500);
  const [searchText, setSearchText] = useState('');
  const [dateRange, setDateRange] = useState<[dayjs.Dayjs | null, dayjs.Dayjs | null] | null>(null);
  const [batchCheckOutModal, setBatchCheckOutModal] = useState(false);
  const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);

  const changeViewMode = (mode: OccupancyView) => {
    setViewMode(mode);
    setSelectedRowKeys([]);
  };
  const changeDateRange = (dates: [dayjs.Dayjs | null, dayjs.Dayjs | null] | null) => {
    setDateRange(dates);
    setSelectedRowKeys([]);
  };

  const {
    data: fetchResult = { data: [], students: [], rooms: [] },
    isLoading,
    isFetching,
    isError,
    refetch,
  } = useQuery<{ data: OccupancyRow[]; students: StudentLookupRow[]; rooms: RoomOverviewRow[] }>({
    queryKey: ['occupancies', viewMode, dateRange],
    queryFn: async () => {
      const [occRes, stuRes, rmRes] = await Promise.allSettled([
        api.get<OccupancyRow[]>('/occupancies', {
          params: {
            ...occupancyParamsForView(viewMode),
            dateFrom: dateRange?.[0]?.format('YYYY-MM-DD'),
            dateTo: dateRange?.[1]?.format('YYYY-MM-DD'),
          },
        }),
        api.get<StudentLookupRow[]>('/students/basic-lookups'),
        api.get<RoomOverviewRow[]>('/rooms/overview'),
      ]);
      const labels = ['入住数据', '学生列表', '房间列表'];
      [stuRes, rmRes].forEach((res, i) => {
        if (res.status === 'rejected') {
          message.warning(`${labels[i + 1]}加载失败`);
        }
      });
      if (occRes.status === 'rejected') {
        throw occRes.reason;
      }
      return {
        data: validateResponse<OccupancyRow[]>(occupanciesSchema, occRes.value),
        students: stuRes.status === 'fulfilled' ? stuRes.value : [],
        rooms: rmRes.status === 'fulfilled' ? rmRes.value : [],
      };
    },
  });
  const data = fetchResult.data;
  const students = fetchResult.students;
  const rooms = fetchResult.rooms;
  const loading = isLoading || isFetching;
  // RouteKeeper 保活页面切回时刷新入住列表
  useVisibleRefetch(['occupancies']);

  const {
    checkInMutation,
    checkOutMutation,
    transferMutation,
    batchCheckOutMutation,
    batchDeleteMutation,
    batchRestoreMutation,
    archiveMutation,
    purgeMutation,
    batchPurgeMutation,
    importMutation,
  } = useOccupancyMutations();
  const [saving, setSaving] = useState(false);
  const [batchLoading, setBatchLoading] = useState(false);
  const [checkInForm] = Form.useForm();
  const [checkOutForm] = Form.useForm();
  const [transferForm] = Form.useForm();
  const [batchCheckOutForm] = Form.useForm();
  const [availableBeds, setAvailableBeds] = useState<any[]>([]);
  const [availableLockers, setAvailableLockers] = useState<any[]>([]);
  const [availableResourcesLoading, setAvailableResourcesLoading] = useState(false);
  const [transferAvailableBeds, setTransferAvailableBeds] = useState<any[]>([]);
  const [transferAvailableLockers, setTransferAvailableLockers] = useState<any[]>([]);
  const [transferResourcesLoading, setTransferResourcesLoading] = useState(false);
  const selectedCheckInRoomId = Form.useWatch('roomId', checkInForm);
  const selectedTransferRoomId = Form.useWatch('newRoomId', transferForm);

  const activeOccupancyByStudentId = useMemo(() => {
    const map = new Map<number, OccupancyRow>();
    data.forEach((item) => {
      if (!item.checkOutDate && item.status !== 'archived') map.set(item.studentId, item);
    });
    return map;
  }, [data]);

  const isRoomSelectable = useCallback((room: RoomOverviewRow) => {
    const currentCount = Number(room.currentCount || 0);
    const capacity = Number(room.capacity || 0);
    return room.status !== 'archived' && room.status !== 'maintenance' && currentCount < capacity;
  }, []);

  const roomOptionLabel = useCallback((room: RoomOverviewRow) => {
    const base = `${room.roomNumber} (${room.building || ''}) [${room.currentCount}/${room.capacity}]`;
    if (room.status === 'maintenance') return `${base} · 维修中`;
    if (room.status === 'archived') return `${base} · 已归档`;
    if (Number(room.currentCount || 0) >= Number(room.capacity || 0)) return `${base} · 已满`;
    return base;
  }, []);

  const selectedBatchRecords = useMemo(
    () => data.filter((item) => selectedRowKeys.includes(item.id) && !item.checkOutDate),
    [data, selectedRowKeys],
  );
  const latestSelectedCheckInDate = useMemo(
    () =>
      selectedBatchRecords
        .map((item) => item.checkInDate)
        .filter((date): date is string => Boolean(date))
        .reduce((latest: string | undefined, date) =>
          !latest || date > latest ? date : latest,
        undefined),
    [selectedBatchRecords],
  );
  const latestSelectedBillingStartDate = useMemo(
    () =>
      selectedBatchRecords
        .map((item) => item.billingStartDate || item.checkInDate)
        .filter((date): date is string => Boolean(date))
        .reduce((latest: string | undefined, date) =>
          !latest || date > latest ? date : latest,
        undefined),
    [selectedBatchRecords],
  );

  const dateNotBefore =
    (start: string | Dayjs | null | undefined, messageText: string) =>
    (_: unknown, value?: Dayjs | null) => {
      if (!value || !start) return Promise.resolve();
      const startDate = dayjs.isDayjs(start) ? start : dayjs(start);
      return value.isBefore(startDate, 'day')
        ? Promise.reject(new Error(messageText))
        : Promise.resolve();
    };

  const handleRoomChange = async (roomId: number) => {
    checkInForm.setFieldValue('bedId', undefined);
    checkInForm.setFieldValue('lockerId', undefined);
    setAvailableBeds([]);
    setAvailableLockers([]);
    if (!roomId) return;
    setAvailableResourcesLoading(true);
    try {
      const [beds, lockers] = await Promise.all([
        api.get<any[]>(`/rooms/${roomId}/beds/available`),
        api.get<any[]>(`/rooms/${roomId}/lockers/available`),
      ]);
      setAvailableBeds(beds);
      setAvailableLockers(lockers);
      if (beds.length === 1) checkInForm.setFieldValue('bedId', beds[0].id);
      if (beds.length === 0) message.warning('该宿舍暂无可用床位，请先在宿舍详情添加或释放床位');
    } catch (e: any) {
      console.error(e);
      setAvailableBeds([]);
      setAvailableLockers([]);
      message.error(e?.message || '宿舍床位和柜子加载失败');
    } finally {
      setAvailableResourcesLoading(false);
    }
  };

  const handleTransferRoomChange = async (roomId: number) => {
    transferForm.setFieldValue('newBedId', undefined);
    transferForm.setFieldValue('newLockerId', undefined);
    setTransferAvailableBeds([]);
    setTransferAvailableLockers([]);
    if (!roomId) return;
    setTransferResourcesLoading(true);
    try {
      const [beds, lockers] = await Promise.all([
        api.get<any[]>(`/rooms/${roomId}/beds/available`),
        api.get<any[]>(`/rooms/${roomId}/lockers/available`),
      ]);
      setTransferAvailableBeds(beds);
      setTransferAvailableLockers(lockers);
      if (beds.length === 1) transferForm.setFieldValue('newBedId', beds[0].id);
      if (beds.length === 0) message.warning('目标宿舍暂无可用床位，请先在宿舍详情添加或释放床位');
    } catch (e: any) {
      console.error(e);
      setTransferAvailableBeds([]);
      setTransferAvailableLockers([]);
      message.error(e?.message || '目标宿舍床位和柜子加载失败');
    } finally {
      setTransferResourcesLoading(false);
    }
  };

  const filteredData = useMemo(() => {
    if (!searchText) return data;
    const keyword = searchText.toLowerCase();
    return data.filter(
      (r: any) =>
        r.student?.name?.toLowerCase().includes(keyword) ||
        r.room?.roomNumber?.toLowerCase().includes(keyword),
    );
  }, [data, searchText]);

  const handleCheckIn = async () => {
    const values = await checkInForm.validateFields();
    setSaving(true);
    try {
      await checkInMutation.mutateAsync(buildCheckInPayload(values));
      message.success('入住登记成功');
      setCheckInModal(false);
      checkInForm.resetFields();
      // 引导业务闭环的下一步：录费用 → 生成账单
      setNextStepHint('billing');
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setSaving(false);
    }
  };

  const handleCheckOut = async () => {
    const values = await checkOutForm.validateFields();
    setSaving(true);
    try {
      await checkOutMutation.mutateAsync({
        id: checkOutModal.id,
        payload: {
          checkOutDate: values.checkOutDate.format('YYYY-MM-DD'),
          billingEndDate: values.billingEndDate?.format('YYYY-MM-DD'),
          checkOutReason: values.checkOutReason,
        },
      });
      message.success('退宿成功');
      setCheckOutModal(null);
      checkOutForm.resetFields();
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setSaving(false);
    }
  };

  const handleTransfer = async () => {
    const values = await transferForm.validateFields();
    setSaving(true);
    try {
      await transferMutation.mutateAsync({
        id: transferModal.id,
        payload: buildTransferPayload(values),
      });
      message.success('换房成功');
      setTransferModal(null);
      transferForm.resetFields();
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setSaving(false);
    }
  };

  const handleBatchCheckOut = async () => {
    if (batchLoading) return;
    const values = await batchCheckOutForm.validateFields();
    setBatchLoading(true);
    try {
      const res: any = await batchCheckOutMutation.mutateAsync({
        ids: selectedRowKeys,
        checkOutDate: values.checkOutDate.format('YYYY-MM-DD'),
        billingEndDate: values.billingEndDate?.format('YYYY-MM-DD'),
        checkOutReason: values.checkOutReason,
      });
      message.success(res.message || `已成功退宿 ${res.success} 人`);
      setBatchCheckOutModal(false);
      batchCheckOutForm.resetFields();
      setSelectedRowKeys([]);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setBatchLoading(false);
    }
  };

  const handleBatchDelete = async () => {
    if (batchLoading) return;
    setBatchLoading(true);
    try {
      const res: any = await batchDeleteMutation.mutateAsync(selectedRowKeys);
      message.success(res?.message || `已归档 ${selectedRowKeys.length} 条`);
      setSelectedRowKeys([]);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setBatchLoading(false);
    }
  };

  const handleBatchRestore = async () => {
    if (batchLoading) return;
    setBatchLoading(true);
    try {
      const res = await batchRestoreMutation.mutateAsync(selectedRowKeys);
      message.success(
        `已恢复 ${res.restored} 条入住记录${res.skipped ? `，跳过 ${res.skipped} 条` : ''}`,
      );
      setSelectedRowKeys([]);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setBatchLoading(false);
    }
  };

  const handlePurge = useCallback(
    (id: number, studentName: string) => {
      modal.confirm({
        title: `永久删除入住记录（${studentName}）？`,
        content: '删除后不可恢复，该入住记录将被物理删除。确定继续？',
        okText: '永久删除',
        okButtonProps: { danger: true },
        cancelText: '取消',
        onOk: async () => {
          try {
            await purgeMutation.mutateAsync(id);
            message.success('已永久删除（不可恢复）');
          } catch {
            // 错误提示由 useApiMutation 统一处理
          }
        },
      });
    },
    [modal, purgeMutation],
  );

  const handleBatchPurge = async () => {
    if (batchLoading) return;
    setBatchLoading(true);
    try {
      const res: any = await batchPurgeMutation.mutateAsync(selectedRowKeys);
      message.success(res?.message || `已永久删除 ${selectedRowKeys.length} 条`);
      setSelectedRowKeys([]);
    } catch {
      // 错误提示由 useApiMutation 统一处理
    } finally {
      setBatchLoading(false);
    }
  };

  const columns = useMemo(
    () =>
      buildOccupancyColumns({
        readonly: viewPolicy.readonly,
        canPurge,
        canDelete,
        onPurge: handlePurge,
        onArchive: (id) => archiveMutation.mutateAsync(id),
        onCheckOut: (record) => {
          setCheckOutModal(record);
          checkOutForm.setFieldsValue({ checkOutDate: dayjs() });
        },
        onTransfer: (record) => {
          setTransferAvailableBeds([]);
          setTransferAvailableLockers([]);
          transferForm.resetFields();
          setTransferModal(record);
          transferForm.setFieldsValue({ transferDate: dayjs() });
        },
      }),
    [
      viewPolicy.readonly,
      canPurge,
      canDelete,
      handlePurge,
      archiveMutation,
      checkOutForm,
      transferForm,
    ],
  );

  const rowSelection = useMemo(
    () => ({
      selectedRowKeys,
      onChange: (keys: any[]) => setSelectedRowKeys(keys),
      getCheckboxProps: (record: any) =>
        viewMode === 'active' ? { disabled: !!record.checkOutDate } : {},
    }),
    [selectedRowKeys, viewMode],
  );

  return (
    <div>
      <Alert
        title="一站式导入"
        description="导入入住名单时会优先按手机号关联已有学生，所属机构自动取学生档案；未找到学生或宿舍时会自动创建。后续仅需在此页面处理换房/退宿等日常操作即可。"
        type="info"
        showIcon
        closable
        style={{ marginBottom: 16 }}
      />
      <OccupanciesToolbar
        viewMode={viewMode}
        onChangeViewMode={changeViewMode}
        onSearch={setSearchText}
        dateRange={dateRange}
        onChangeDateRange={changeDateRange}
        canCheckIn={canCheckIn}
        onCheckIn={() => {
          checkInForm.resetFields();
          setAvailableBeds([]);
          setAvailableLockers([]);
          setAvailableResourcesLoading(false);
          const today = dayjs();
          checkInForm.setFieldsValue({
            checkInDate: today,
            billingStartDate: today,
            stayType: 'short',
            collectDeposit: true,
            depositAmount: 500,
          });
          setCheckInModal(true);
        }}
        onImport={async ({ file, onSuccess, onError }: any) => {
          const formData = new FormData();
          formData.append('file', file);
          const params = new URLSearchParams();
          if (autoDeposit) { params.set('autoDeposit', 'true'); params.set('depositAmount', String(depositAmount)); }
          try {
            const res: any = await importMutation.mutateAsync({
              formData,
              params: params.toString(),
            });
            if (res.errors?.length > 0) {
              modal.warning({
                title: res.message,
                content: res.errors.join('\n'),
                width: 500,
              });
            } else {
              message.success(res.message);
            }
            onSuccess?.(res);
          } catch (e) {
            onError?.(e as Error);
          }
        }}
        autoDeposit={autoDeposit}
        onAutoDepositChange={setAutoDeposit}
        depositAmount={depositAmount}
        onDepositAmountChange={setDepositAmount}
        onDownloadTemplate={() => {
          void import('../../utils/download').then(({ downloadBlob }) =>
            downloadBlob('/occupancies/template', '入住名单导入模板.xlsx').catch(() => message.error('下载失败')),
          );
        }}
        onExport={() => {
          void import('../../utils/download').then(({ downloadBlob }) => {
            const params = viewMode === 'active' ? '?active=true' : '';
            const filename = viewMode === 'active' ? '在住记录.xlsx' : '全部入住记录.xlsx';
            downloadBlob('/occupancies/export' + params, filename).catch(() => message.error('导出失败'));
          });
        }}
      />
      {nextStepHint === 'billing' && (
        <NextStepHint
          title="入住登记完成"
          description="接下来可以为该学生录入公共/个人费用，再生成账单完成计费闭环。"
          action={{ label: '去费用管理', onClick: () => navigate('/expenses') }}
          onClose={() => setNextStepHint(null)}
        />
      )}
      {isError ? (
        <QueryErrorState
          title="入住数据加载失败"
          description="请检查网络后重试。"
          onRetry={() => void refetch()}
        />
      ) : (
        <OccupanciesTableArea
          columns={columns}
          data={filteredData}
          loading={loading}
          selectedRowKeys={selectedRowKeys}
          rowSelection={rowSelection}
          batchAction={viewPolicy.batchAction}
          canDelete={canDelete}
          canPurge={canPurge}
          batchLoading={batchLoading}
          onBatchCheckOut={() => {
            batchCheckOutForm.resetFields();
            batchCheckOutForm.setFieldsValue({ checkOutDate: dayjs() });
            setBatchCheckOutModal(true);
          }}
          onBatchDelete={handleBatchDelete}
          onBatchRestore={handleBatchRestore}
          onBatchPurge={handleBatchPurge}
          onClearSelection={() => setSelectedRowKeys([])}
        />
      )}

      <CheckInModal
        open={checkInModal}
        canCheckIn={canCheckIn}
        saving={saving}
        form={checkInForm}
        students={students}
        activeOccupancyByStudentId={activeOccupancyByStudentId}
        rooms={rooms}
        roomOptionLabel={roomOptionLabel}
        isRoomSelectable={isRoomSelectable}
        onRoomChange={handleRoomChange}
        availableBeds={availableBeds}
        availableLockers={availableLockers}
        availableResourcesLoading={availableResourcesLoading}
        selectedCheckInRoomId={selectedCheckInRoomId}
        dateNotBefore={dateNotBefore}
        onOk={handleCheckIn}
        onCancel={() => { setCheckInModal(false); setAvailableBeds([]); setAvailableLockers([]); setAvailableResourcesLoading(false); }}
      />
      <CheckOutModal
        record={checkOutModal}
        canCheckOut={canCheckOut}
        saving={saving}
        form={checkOutForm}
        dateNotBefore={dateNotBefore}
        onOk={handleCheckOut}
        onCancel={() => setCheckOutModal(null)}
      />
      <BatchCheckOutModal
        open={batchCheckOutModal}
        canCheckOut={canCheckOut}
        selectedRowKeys={selectedRowKeys}
        latestSelectedCheckInDate={latestSelectedCheckInDate}
        latestSelectedBillingStartDate={latestSelectedBillingStartDate}
        data={data}
        form={batchCheckOutForm}
        dateNotBefore={dateNotBefore}
        onOk={handleBatchCheckOut}
        onCancel={() => setBatchCheckOutModal(false)}
      />
      <TransferModal
        record={transferModal}
        canTransfer={canTransfer}
        saving={saving}
        form={transferForm}
        rooms={rooms}
        roomOptionLabel={roomOptionLabel}
        isRoomSelectable={isRoomSelectable}
        onRoomChange={handleTransferRoomChange}
        transferAvailableBeds={transferAvailableBeds}
        transferAvailableLockers={transferAvailableLockers}
        transferResourcesLoading={transferResourcesLoading}
        selectedTransferRoomId={selectedTransferRoomId}
        dateNotBefore={dateNotBefore}
        onOk={handleTransfer}
        onCancel={() => { setTransferModal(null); transferForm.resetFields(); setTransferAvailableBeds([]); setTransferAvailableLockers([]); }}
      />
    </div>
  );
};

export default OccupanciesPage;
