"use client";

import { useTranslation } from "react-i18next";
import React, { useEffect, useMemo } from "react";
import RestartAlt from "@mui/icons-material/RestartAlt";
import SearchResults, { Column } from "../SearchResults";
import { Search, Clear, PersonAdd, PersonRemove } from "@mui/icons-material";
import {
  Stack,
  Typography,
  Grid,
  TextField,
  InputAdornment,
  IconButton,
  FormControl,
  InputLabel,
  Select,
  MenuItem,
  Box,
  Button,
  Card,
  CardActions,
  CardContent,
  TabsProps,
  Tab,
  Tabs,
  SelectChangeEvent,
} from "@mui/material";
import differenceWith from "lodash/differenceWith";
import intersectionWith from "lodash/intersectionWith";
import uniq from "lodash/uniq";
import ResourceCapacity from "./ResourceCapacity";
import { useFormContext } from "react-hook-form";
import { CreateProjectInputs } from "@/app/api/projects/actions";
import ResourceAllocation from "./ResourceAllocation";
import { Task } from "@/app/api/tasks";
import { Grade } from "@/app/api/grades";
import { StaffResult } from "@/app/api/staff";

const staffComparator = (a: StaffResult, b: StaffResult) => {
  return (
    a.team?.localeCompare(b.team) ||
    a.grade?.localeCompare(b.grade) ||
    a.id - b.id
  );
};

export interface Props {
  allStaffs: StaffResult[];
  isActive: boolean;
  defaultManhourBreakdownByGrade?: { [gradeId: number]: number };
  allTasks: Task[];
  grades: Grade[];
}

const StaffAllocation: React.FC<Props> = ({
  allStaffs,
  allTasks,
  isActive,
  defaultManhourBreakdownByGrade,
  grades,
}) => {
  const { t } = useTranslation();
  const { setValue, getValues, watch } = useFormContext<CreateProjectInputs>();

  const [filteredStaff, setFilteredStaff] = React.useState(
    allStaffs.sort(staffComparator),
  );
  const selectedStaffIds = watch("allocatedStaffIds");

  // Adding / Removing staff
  const addStaff = React.useCallback(
    (staff: StaffResult) => {
      const currentStaffIds = getValues("allocatedStaffIds");
      setValue("allocatedStaffIds", [...currentStaffIds, staff.id]);
    },
    [getValues, setValue],
  );
  const removeStaff = React.useCallback(
    (staff: StaffResult) => {
      const currentStaffIds = getValues("allocatedStaffIds");
      setValue(
        "allocatedStaffIds",
        currentStaffIds.filter((id) => id !== staff.id),
      );
    },
    [getValues, setValue],
  );
  const clearStaff = React.useCallback(() => {
    setValue("allocatedStaffIds", []);
  }, [setValue]);

  const staffPoolColumns = React.useMemo<Column<StaffResult>[]>(
    () => [
      {
        label: t("Add"),
        name: "id",
        onClick: addStaff,
        buttonIcon: <PersonAdd />,
      },
      { label: t("Team"), name: "team" },
      { label: t("Grade"), name: "grade" },
      { label: t("Staff ID"), name: "staffId" },
      { label: t("Staff Name"), name: "name" },
      { label: t("Title"), name: "currentPosition" },
    ],
    [addStaff, t],
  );

  const allocatedStaffColumns = React.useMemo<Column<StaffResult>[]>(
    () => [
      {
        label: t("Remove"),
        name: "id",
        onClick: removeStaff,
        buttonIcon: <PersonRemove />,
      },
      { label: t("Team"), name: "team" },
      { label: t("Grade"), name: "grade" },
      { label: t("Staff ID"), name: "id" },
      { label: t("Staff Name"), name: "name" },
      { label: t("Title"), name: "currentPosition" },
    ],
    [removeStaff, t],
  );

  // Query related
  const [query, setQuery] = React.useState("");
  const onQueryInputChange = React.useCallback<
    React.ChangeEventHandler<HTMLInputElement>
  >((e) => {
    setQuery(e.target.value);
  }, []);
  const clearQueryInput = React.useCallback(() => {
    setQuery("");
  }, []);
  const columnFilters = React.useMemo<(keyof StaffResult)[]>(
    () => ["team", "grade"],
    [],
  );
  const filterValues = React.useMemo(() => {
    return columnFilters.reduce<{ [filter in keyof StaffResult]?: string[] }>(
      (acc, filter) => {
        return {
          ...acc,
          [filter]: uniq(allStaffs.map((staff) => staff[filter])),
        };
      },
      {},
    );
  }, [columnFilters, allStaffs]);
  const defaultFilterValues = React.useMemo(() => {
    return columnFilters.reduce<{ [filter in keyof StaffResult]?: string }>(
      (acc, filter) => {
        return { ...acc, [filter]: "All" };
      },
      {},
    );
  }, [columnFilters]);
  const [filters, setFilters] = React.useState(defaultFilterValues);
  const makeFilterSelect = React.useCallback(
    (filter: keyof StaffResult) => (event: SelectChangeEvent<string>) => {
      setFilters((f) => ({ ...f, [filter]: event.target.value }));
    },
    [],
  );

  useEffect(() => {
    setFilteredStaff(
      allStaffs.filter((staff) => {
        const q = query.toLowerCase();
        return (
          (staff.name.toLowerCase().includes(q) ||
            staff.id.toString().includes(q) ||
            staff.currentPosition.toLowerCase().includes(q)) &&
          Object.entries(filters).every(([filterKey, filterValue]) => {
            const staffColumnValue = staff[filterKey as keyof StaffResult];
            return staffColumnValue === filterValue || filterValue === "All";
          })
        );
      }),
    );
  }, [allStaffs, filters, query]);

  // Tab related
  const [tabIndex, setTabIndex] = React.useState(0);
  const handleTabChange = React.useCallback<NonNullable<TabsProps["onChange"]>>(
    (_e, newValue) => {
      setTabIndex(newValue);
    },
    [],
  );

  const reset = React.useCallback(() => {
    clearQueryInput();
    clearStaff();
    setFilters(defaultFilterValues);
  }, [clearQueryInput, clearStaff, defaultFilterValues]);

  return (
    <>
      <Card sx={{ display: isActive ? "block" : "none" }}>
        <CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
          <ResourceCapacity />
        </CardContent>
      </Card>
      <Card sx={{ display: isActive ? "block" : "none" }}>
        <CardContent sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
          <Stack gap={2}>
            <Typography variant="overline" display="block">
              {t("Staff Allocation")}
            </Typography>
            <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
              <Grid item xs={6} display="flex" alignItems="center">
                <Search sx={{ marginInlineEnd: 1 }} />
                <TextField
                  variant="standard"
                  fullWidth
                  onChange={onQueryInputChange}
                  value={query}
                  placeholder={t("Search by staff ID, name or title")}
                  InputProps={{
                    endAdornment: query && (
                      <InputAdornment position="end">
                        <IconButton onClick={clearQueryInput}>
                          <Clear />
                        </IconButton>
                      </InputAdornment>
                    ),
                  }}
                />
              </Grid>
              {columnFilters.map((filter, idx) => {
                const label = staffPoolColumns.find(
                  (c) => c.name === filter,
                )!.label;

                return (
                  <Grid key={`${filter.toString()}-${idx}`} item xs={3}>
                    <FormControl fullWidth>
                      <InputLabel size="small">{label}</InputLabel>
                      <Select
                        label={label}
                        size="small"
                        value={filters[filter]}
                        onChange={makeFilterSelect(filter)}
                      >
                        <MenuItem value={"All"}>{t("All")}</MenuItem>
                        {filterValues[filter]?.map((option, index) => (
                          <MenuItem key={`${option}-${index}`} value={option}>
                            {option}
                          </MenuItem>
                        ))}
                      </Select>
                    </FormControl>
                  </Grid>
                );
              })}
            </Grid>
            <Tabs value={tabIndex} onChange={handleTabChange}>
              <Tab label={t("Staff Pool")} />
              <Tab
                label={`${t("Allocated Staff")} (${selectedStaffIds.length})`}
              />
            </Tabs>
            <Box sx={{ marginInline: -3 }}>
              {tabIndex === 0 && (
                <SearchResults
                  noWrapper
                  items={differenceWith(
                    filteredStaff,
                    selectedStaffIds,
                    (staff, staffId) => staff.id === staffId,
                  )}
                  columns={staffPoolColumns}
                />
              )}
              {tabIndex === 1 && (
                <SearchResults
                  noWrapper
                  items={intersectionWith(
                    allStaffs,
                    selectedStaffIds,
                    (staff, staffId) => staff.id === staffId,
                  )}
                  columns={allocatedStaffColumns}
                />
              )}
            </Box>
          </Stack>
          <CardActions sx={{ justifyContent: "flex-end" }}>
            <Button variant="text" startIcon={<RestartAlt />} onClick={reset}>
              {t("Reset")}
            </Button>
          </CardActions>
        </CardContent>
      </Card>
      {/* MUI X-Grid will throw an error if it is rendered without any dimensions; so not rendering when not active */}
      {isActive && (
        <Card>
          <CardContent>
            <ResourceAllocation
              grades={grades}
              allTasks={allTasks}
              manhourBreakdownByGrade={defaultManhourBreakdownByGrade}
            />
          </CardContent>
        </Card>
      )}
    </>
  );
};

export default StaffAllocation;
