import React, { useEffect, useState, useCallback } from 'react';
import {
  Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow,
  Paper, Checkbox, FormControl, InputLabel, Select, MenuItem, ToggleButtonGroup,
  ToggleButton, Button, CircularProgress, TablePagination, Typography, Divider,
  Dialog, DialogTitle, DialogContent, DialogActions, DialogContentText, TextField,
  ListItemText, Alert,
} from '@mui/material';
import { adminService } from '../../api/adminService';
import type { AllocationRecord, AdminUser } from '../../api/adminService';
import { accountService } from '../../api/accountService';
import { activityFeedsService } from '../../api/activityFeedsService';
import { useFeedStore } from '../../store/feedStore';
import { useToastStore } from '../../store/toastStore';

interface Organization {
  org_id: string;
  org_name: string;
  db_name: string;
}

interface SiteOption {
  site_id: string;
  site_name: string;
}

// Mirrors the site-parsing logic in ClientsOverview.tsx/MainLayout.tsx —
// /Master/site's response shape varies (responseData[0].records, nested
// sub_sites, or a plain array).
const parseSites = (sitesRes: any): SiteOption[] => {
  let parsedSites: any[] = [];
  if (sitesRes?.responseData?.[0]?.records) {
    parsedSites = sitesRes.responseData[0].records;
    if (parsedSites.length === 1 && Array.isArray(parsedSites[0])) {
      parsedSites = parsedSites[0];
    } else if (parsedSites.length > 0 && Array.isArray(parsedSites[0])) {
      parsedSites = parsedSites.flat();
    }
  } else if (Array.isArray(sitesRes)) {
    parsedSites = sitesRes;
  } else if (sitesRes?.data && Array.isArray(sitesRes.data)) {
    parsedSites = sitesRes.data;
  }

  return parsedSites
    .map((site) => ({
      site_id: site.site_id || (site.sub_sites || []).map((s: any) => s.site_id).filter(Boolean).join(','),
      site_name: site.site_name || site.name || site.siteName || site.site_id,
    }))
    .filter((s) => s.site_id);
};

const PAGE_SIZE = 25;
const BULK_PRESETS = [1000, 2000, 5000, 10000];

// count split equally, remainder to the first users: 100/3 -> 34/33/33
const computeSplit = (count: number, n: number): number[] => {
  const base = Math.floor(count / n);
  const rem = count % n;
  return Array.from({ length: n }, (_, i) => base + (i < rem ? 1 : 0));
};

type ConfirmAction =
  | { kind: 'bulk'; count: number }
  | { kind: 'unassignAll' }
  | { kind: 'moveAll'; toUserId: number }
  | { kind: 'unassignSelected' };

export const Allocation: React.FC = () => {
  const { assets } = useFeedStore();
  const [organizations, setOrganizations] = useState<Organization[]>([]);
  const [dbName, setDbName] = useState('');
  const [type, setType] = useState<'audits' | 'rectification'>('audits');
  const [sort, setSort] = useState<'oldest' | 'newest'>('oldest');

  const [dateFrom, setDateFrom] = useState('');
  const [dateTo, setDateTo] = useState('');
  const [assetId, setAssetId] = useState('');
  const [siteId, setSiteId] = useState('');
  const [sites, setSites] = useState<SiteOption[]>([]);

  const [users, setUsers] = useState<AdminUser[]>([]);
  const [selectedUserIds, setSelectedUserIds] = useState<number[]>([]);
  // '' = all, 'null' = unassigned, otherwise a user_id string
  const [assignedFilter, setAssignedFilter] = useState('');
  const [customCount, setCustomCount] = useState('');
  const [moveTargetId, setMoveTargetId] = useState('');

  const [records, setRecords] = useState<AllocationRecord[]>([]);
  const [total, setTotal] = useState(0);
  const [page, setPage] = useState(0);
  const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());
  const [loading, setLoading] = useState(false);
  const [assigning, setAssigning] = useState(false);
  const [confirmAction, setConfirmAction] = useState<ConfirmAction | null>(null);

  useEffect(() => {
    (async () => {
      try {
        const [orgsRes, usersRes] = await Promise.all([
          accountService.getOrganizations(),
          adminService.getUsers(),
        ]);
        const orgs = (orgsRes as any) || [];
        setOrganizations(orgs);
        setUsers(usersRes.filter((u) => !u.disabled));
        if (orgs.length > 0) setDbName(orgs[0].db_name);
      } catch (err) {
        useToastStore.getState().showToast('Failed to load organizations/users.', 'error');
      }
    })();
  }, []);

  // Sites are org-scoped, and the org being managed here may differ from the
  // admin's own logged-in org, so this refetches whenever the selection changes
  // rather than reusing the global feedStore (which is scoped to the admin's own org).
  useEffect(() => {
    const org = organizations.find((o) => o.db_name === dbName);
    if (!org) return;
    (async () => {
      try {
        const sitesRes = await activityFeedsService.getOrganizationSites(org.org_id);
        setSites(parseSites(sitesRes));
      } catch (err) {
        useToastStore.getState().showToast('Failed to load sites for this organization.', 'error');
      }
    })();
    setSiteId('');
  }, [dbName, organizations]);

  // dateTo is a bare date (e.g. "2026-07-09"); video_date is a full timestamp,
  // so comparing "<= 2026-07-09" would exclude same-day records after
  // midnight - extend it to end-of-day. Also normalize the order: both
  // fields are independent and unset by default, so it's easy to end up with
  // From after To while filling them in, which would silently match zero
  // rows instead of erroring.
  const [effectiveDateFrom, effectiveDateTo] = (() => {
    if (!dateFrom || !dateTo) return [dateFrom || undefined, dateTo ? `${dateTo}T23:59:59` : undefined];
    return dateFrom <= dateTo
      ? [dateFrom, `${dateTo}T23:59:59`]
      : [dateTo, `${dateFrom}T23:59:59`];
  })();

  const load = useCallback(async () => {
    if (!dbName) return;
    setLoading(true);
    try {
      const result = await adminService.getAllocationRecords({
        dbName, type, sort, limit: PAGE_SIZE, offset: page * PAGE_SIZE,
        dateFrom: effectiveDateFrom,
        dateTo: effectiveDateTo,
        assetId: assetId || undefined,
        siteId: siteId || undefined,
        assignedUserId: assignedFilter === '' ? undefined : assignedFilter === 'null' ? 'null' : Number(assignedFilter),
      });
      setRecords(result.records);
      setTotal(result.total);
    } catch (err) {
      useToastStore.getState().showToast('Failed to load records.', 'error');
    } finally {
      setLoading(false);
    }
  }, [dbName, type, sort, page, effectiveDateFrom, effectiveDateTo, assetId, siteId, assignedFilter]);

  useEffect(() => {
    load();
  }, [load]);

  useEffect(() => {
    setPage(0);
    setSelectedIds(new Set());
  }, [dbName, type, sort, dateFrom, dateTo, assetId, siteId, assignedFilter]);

  const toggleRow = (id: number) => {
    setSelectedIds((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  };

  const toggleAll = () => {
    if (selectedIds.size === records.length) {
      setSelectedIds(new Set());
    } else {
      setSelectedIds(new Set(records.map((r) => r.id)));
    }
  };

  const selectedUsers = users.filter((u) => selectedUserIds.includes(u.user_id));
  const selectedOrg = organizations.find((o) => o.db_name === dbName);
  const filterUser = users.find((u) => String(u.user_id) === assignedFilter);

  const perUserToast = (perUser: { user_id: number; assigned: number }[]) =>
    perUser
      .map((p) => `${users.find((u) => u.user_id === p.user_id)?.username || `#${p.user_id}`}: ${p.assigned}`)
      .join(', ');

  const handleAllocateSelected = async () => {
    if (selectedIds.size === 0 || selectedUserIds.length === 0) return;
    setAssigning(true);
    try {
      const result = await adminService.assignAllocation({
        dbName, type, ids: Array.from(selectedIds), user_ids: selectedUserIds,
      });
      useToastStore.getState().showToast(
        `Allocated ${result.assigned} record(s) — ${perUserToast(result.perUser)}`, 'success');
      setSelectedIds(new Set());
      load();
    } catch (err: any) {
      useToastStore.getState().showToast(err?.response?.data?.message || 'Failed to allocate records.', 'error');
    } finally {
      setAssigning(false);
    }
  };

  const handleBulkAllocate = async (count: number) => {
    if (selectedUserIds.length === 0) return;
    setConfirmAction(null);
    setAssigning(true);
    try {
      const result = await adminService.assignAllocation({
        dbName, type, count, sort, user_ids: selectedUserIds,
        dateFrom: effectiveDateFrom,
        dateTo: effectiveDateTo,
        assetId: assetId || undefined,
        siteId: siteId || undefined,
      });
      useToastStore.getState().showToast(
        `Allocated ${result.assigned} record(s) — ${perUserToast(result.perUser)}`, 'success');
      setSelectedIds(new Set());
      setPage(0);
      load();
    } catch (err: any) {
      useToastStore.getState().showToast(err?.response?.data?.message || 'Failed to bulk-allocate records.', 'error');
    } finally {
      setAssigning(false);
    }
  };

  const handleReassign = async (params: { fromUserId?: number; toUserId: number | null; ids?: number[] }) => {
    setConfirmAction(null);
    setAssigning(true);
    try {
      const result = await adminService.reassignAllocation({ dbName, type, ...params });
      const verb = params.toUserId === null ? 'Unassigned' : 'Moved';
      useToastStore.getState().showToast(`${verb} ${result.moved} pending record(s).`, 'success');
      setSelectedIds(new Set());
      setMoveTargetId('');
      load();
    } catch (err: any) {
      useToastStore.getState().showToast(err?.response?.data?.message || 'Failed to update allocation.', 'error');
    } finally {
      setAssigning(false);
    }
  };

  const customCountNum = parseInt(customCount, 10);
  const customCountValid = Number.isInteger(customCountNum) && customCountNum > 0 && customCountNum <= 10000;

  const splitPreview = (count: number) => {
    if (selectedUsers.length === 0) return '';
    const split = computeSplit(count, selectedUsers.length);
    return selectedUsers.map((u, i) => `${u.username}: ${split[i]}`).join('   ·   ');
  };

  return (
    <Box>
      <Box sx={{ display: 'flex', gap: 2, mb: 2, flexWrap: 'wrap', alignItems: 'center' }}>
        <FormControl size="small" sx={{ minWidth: 180 }}>
          <InputLabel>Organization</InputLabel>
          <Select label="Organization" value={dbName} onChange={(e) => setDbName(e.target.value)}>
            {organizations.map((o) => <MenuItem key={o.org_id} value={o.db_name}>{o.org_name}</MenuItem>)}
          </Select>
        </FormControl>

        <ToggleButtonGroup
          value={type}
          exclusive
          onChange={(_, value) => value && setType(value)}
          size="small"
        >
          <ToggleButton value="audits">Audits</ToggleButton>
          <ToggleButton value="rectification">Rectification</ToggleButton>
        </ToggleButtonGroup>

        <ToggleButtonGroup
          value={sort}
          exclusive
          onChange={(_, value) => value && setSort(value)}
          size="small"
        >
          <ToggleButton value="oldest">Oldest first</ToggleButton>
          <ToggleButton value="newest">Newest first</ToggleButton>
        </ToggleButtonGroup>
      </Box>

      <Box sx={{ display: 'flex', gap: 2, mb: 3, flexWrap: 'wrap', alignItems: 'center' }}>
        <TextField
          label="Video date from"
          type="date"
          size="small"
          value={dateFrom}
          onChange={(e) => setDateFrom(e.target.value)}
          slotProps={{ inputLabel: { shrink: true } }}
        />
        <TextField
          label="Video date to"
          type="date"
          size="small"
          value={dateTo}
          onChange={(e) => setDateTo(e.target.value)}
          slotProps={{ inputLabel: { shrink: true } }}
        />
        <FormControl size="small" sx={{ minWidth: 200 }}>
          <InputLabel>Asset</InputLabel>
          <Select label="Asset" value={assetId} onChange={(e) => setAssetId(e.target.value)}>
            <MenuItem value="">All assets</MenuItem>
            {assets.map((a: any) => <MenuItem key={a.id} value={a.id}>{a.name}</MenuItem>)}
          </Select>
        </FormControl>
        <FormControl size="small" sx={{ minWidth: 200 }}>
          <InputLabel>Site</InputLabel>
          <Select label="Site" value={siteId} onChange={(e) => setSiteId(e.target.value)}>
            <MenuItem value="">All sites</MenuItem>
            {sites.map((s) => <MenuItem key={s.site_id} value={s.site_id}>{s.site_name}</MenuItem>)}
          </Select>
        </FormControl>
        <FormControl size="small" sx={{ minWidth: 200 }}>
          <InputLabel>Assigned to</InputLabel>
          <Select label="Assigned to" value={assignedFilter} onChange={(e) => setAssignedFilter(e.target.value)}>
            <MenuItem value="">All records</MenuItem>
            <MenuItem value="null">Unassigned only</MenuItem>
            {users.map((u) => <MenuItem key={u.user_id} value={String(u.user_id)}>{u.username}</MenuItem>)}
          </Select>
        </FormControl>
      </Box>

      {/* Absent-user actions: visible when viewing one user's queue */}
      {filterUser && (
        <Alert severity="info" sx={{ mb: 2, alignItems: 'center' }}
          action={
            <Box sx={{ display: 'flex', gap: 1.5, alignItems: 'center' }}>
              <Button
                size="small"
                color="warning"
                variant="outlined"
                disabled={assigning || total === 0}
                onClick={() => setConfirmAction({ kind: 'unassignAll' })}
              >
                Unassign all pending
              </Button>
              <FormControl size="small" sx={{ minWidth: 170 }}>
                <InputLabel>Move all pending to</InputLabel>
                <Select
                  label="Move all pending to"
                  value={moveTargetId}
                  disabled={assigning || total === 0}
                  onChange={(e) => {
                    const target = Number(e.target.value);
                    setMoveTargetId(e.target.value);
                    setConfirmAction({ kind: 'moveAll', toUserId: target });
                  }}
                >
                  {users.filter((u) => String(u.user_id) !== assignedFilter).map((u) => (
                    <MenuItem key={u.user_id} value={String(u.user_id)}>{u.username}</MenuItem>
                  ))}
                </Select>
              </FormControl>
            </Box>
          }
        >
          {total.toLocaleString()} pending record(s) assigned to <strong>{filterUser.username}</strong>
          {' '}(matching current filters)
        </Alert>
      )}

      <FormControl size="small" sx={{ minWidth: 280, mb: 2 }}>
        <InputLabel>Allocate to (select one or more)</InputLabel>
        <Select
          multiple
          label="Allocate to (select one or more)"
          value={selectedUserIds}
          onChange={(e) => setSelectedUserIds(typeof e.target.value === 'string' ? [] : (e.target.value as number[]))}
          renderValue={(ids) =>
            users.filter((u) => (ids as number[]).includes(u.user_id)).map((u) => u.username).join(', ')
          }
        >
          {users.map((u) => (
            <MenuItem key={u.user_id} value={u.user_id}>
              <Checkbox checked={selectedUserIds.includes(u.user_id)} size="small" />
              <ListItemText primary={`${u.username} (${u.role_name})`} />
            </MenuItem>
          ))}
        </Select>
      </FormControl>

      <Box sx={{ display: 'flex', gap: 2, mb: 1, alignItems: 'center', flexWrap: 'wrap' }}>
        <Typography variant="body2">{selectedIds.size} selected on this page</Typography>
        <Button
          variant="contained"
          disabled={selectedIds.size === 0 || selectedUserIds.length === 0 || assigning}
          onClick={handleAllocateSelected}
        >
          {selectedUserIds.length > 1 ? 'Split selected equally' : 'Allocate selected'}
        </Button>
        <Button
          variant="outlined"
          color="warning"
          disabled={selectedIds.size === 0 || assigning}
          onClick={() => setConfirmAction({ kind: 'unassignSelected' })}
        >
          Unassign selected
        </Button>
      </Box>

      <Box sx={{ display: 'flex', gap: 1.5, mb: 3, alignItems: 'center', flexWrap: 'wrap' }}>
        <Typography variant="body2" sx={{ color: 'text.secondary' }}>
          Bulk assign unassigned records (total, split equally · matches current filters):
        </Typography>
        {BULK_PRESETS.map((count) => (
          <Button
            key={count}
            variant="outlined"
            size="small"
            disabled={selectedUserIds.length === 0 || assigning}
            onClick={() => setConfirmAction({ kind: 'bulk', count })}
          >
            {count.toLocaleString()}
          </Button>
        ))}
        <TextField
          label="Custom count"
          size="small"
          type="number"
          value={customCount}
          onChange={(e) => setCustomCount(e.target.value)}
          sx={{ width: 130 }}
          slotProps={{ htmlInput: { min: 1, max: 10000 } }}
        />
        <Button
          variant="outlined"
          size="small"
          disabled={selectedUserIds.length === 0 || !customCountValid || assigning}
          onClick={() => setConfirmAction({ kind: 'bulk', count: customCountNum })}
        >
          Assign
        </Button>
      </Box>

      <Divider sx={{ mb: 2 }} />

      {loading ? (
        <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}><CircularProgress /></Box>
      ) : (
        <TableContainer component={Paper}>
          <Table size="small">
            <TableHead>
              <TableRow>
                <TableCell padding="checkbox">
                  <Checkbox
                    checked={records.length > 0 && selectedIds.size === records.length}
                    indeterminate={selectedIds.size > 0 && selectedIds.size < records.length}
                    onChange={toggleAll}
                  />
                </TableCell>
                <TableCell>Anomaly ID</TableCell>
                <TableCell>Asset</TableCell>
                <TableCell>Plaza</TableCell>
                <TableCell>Chainage</TableCell>
                <TableCell>Video Date</TableCell>
                <TableCell>Assigned To</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {records.map((r) => (
                <TableRow key={r.id} selected={selectedIds.has(r.id)}>
                  <TableCell padding="checkbox">
                    <Checkbox checked={selectedIds.has(r.id)} onChange={() => toggleRow(r.id)} />
                  </TableCell>
                  <TableCell>{r.id}</TableCell>
                  <TableCell>{r.Asset || '—'}</TableCell>
                  <TableCell>{r.Plaza || '—'}</TableCell>
                  <TableCell>{r.Chainage || '—'}</TableCell>
                  <TableCell>{r.video_date ? new Date(r.video_date).toLocaleDateString() : '—'}</TableCell>
                  <TableCell>{r.assigned_username || '—'}</TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
          <TablePagination
            component="div"
            count={total}
            page={page}
            onPageChange={(_, newPage) => setPage(newPage)}
            rowsPerPage={PAGE_SIZE}
            rowsPerPageOptions={[PAGE_SIZE]}
          />
        </TableContainer>
      )}

      <Dialog open={confirmAction !== null} onClose={() => { setConfirmAction(null); setMoveTargetId(''); }}>
        <DialogTitle>
          {confirmAction?.kind === 'bulk' && 'Confirm bulk allocation'}
          {confirmAction?.kind === 'unassignAll' && 'Unassign all pending'}
          {confirmAction?.kind === 'moveAll' && 'Move all pending'}
          {confirmAction?.kind === 'unassignSelected' && 'Unassign selected'}
        </DialogTitle>
        <DialogContent>
          {confirmAction?.kind === 'bulk' && (
            <DialogContentText component="div">
              Split the first {confirmAction.count.toLocaleString()} {sort} <strong>unassigned</strong> {type} record(s) in{' '}
              {selectedOrg?.org_name || dbName} (matching the current date/asset/site filters) equally:
              <Box sx={{ mt: 1.5, fontFamily: 'monospace', fontSize: '0.9rem' }}>
                {splitPreview(confirmAction.count)}
              </Box>
              <Box sx={{ mt: 1.5 }}>
                If fewer records are available, users earlier in the list are filled first.
              </Box>
            </DialogContentText>
          )}
          {confirmAction?.kind === 'unassignAll' && (
            <DialogContentText>
              Remove the assignment from all {total.toLocaleString()} pending record(s) currently allocated to{' '}
              <strong>{filterUser?.username}</strong> in {selectedOrg?.org_name || dbName}? Records they already
              audited keep their name for history/efficiency stats.
            </DialogContentText>
          )}
          {confirmAction?.kind === 'moveAll' && (
            <DialogContentText>
              Move all {total.toLocaleString()} pending record(s) from <strong>{filterUser?.username}</strong> to{' '}
              <strong>{users.find((u) => u.user_id === confirmAction.toUserId)?.username}</strong>? Records already
              audited by {filterUser?.username} keep their name for history/efficiency stats.
            </DialogContentText>
          )}
          {confirmAction?.kind === 'unassignSelected' && (
            <DialogContentText>
              Remove the assignment from the {selectedIds.size} selected pending record(s)?
            </DialogContentText>
          )}
        </DialogContent>
        <DialogActions>
          <Button onClick={() => { setConfirmAction(null); setMoveTargetId(''); }}>Cancel</Button>
          <Button
            variant="contained"
            onClick={() => {
              if (!confirmAction) return;
              if (confirmAction.kind === 'bulk') handleBulkAllocate(confirmAction.count);
              else if (confirmAction.kind === 'unassignAll') handleReassign({ fromUserId: Number(assignedFilter), toUserId: null });
              else if (confirmAction.kind === 'moveAll') handleReassign({ fromUserId: Number(assignedFilter), toUserId: confirmAction.toUserId });
              else if (confirmAction.kind === 'unassignSelected') handleReassign({ toUserId: null, ids: Array.from(selectedIds) });
            }}
          >
            Confirm
          </Button>
        </DialogActions>
      </Dialog>
    </Box>
  );
};
