import * as React from "react";
import {
  Box,
  Container,
  CircularProgress,
  Alert,
  TextField,
  Paper,
  Autocomplete,
  Button
} from "@mui/material";

import DashboardView from "./components/Dashboard";

import {
  DashboardState,
  DashboardStateSetters,
  DashboardFlow,
} from "./components/Dashboard";

import { configuration } from "./dashboard-config";
import {
  useReport,
  prepareReport,
} from "./features/report";
import { useReportSnapshotsList } from "./features/report-snapshots";

function formatSnapshotDate(iso: string) {
  const d = new Date(iso);
  return d.toLocaleString(undefined, {
    year: "numeric",
    month: "short",
    day: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

export default function Dashboard() {
  const [state, setState] = React.useState<DashboardState>({
    flow: "outflows",
    periodType: "rolling",
    selectedPeriodId: null,
    selectedGroupKey: null,
    comparison: false,
  });

  const [appliedPayees, setAppliedPayees] = React.useState<string[]>([]);
  const [appliedTags, setAppliedTags] = React.useState<string[]>([]);

  const [payeeInput, setPayeeInput] = React.useState<string[]>([]);
  const [tagsInput, setTagsInput] = React.useState<string[]>([]);

  const [loadedPayees, setLoadedPayees] = React.useState<string[]>([]);
  const [loadedTags, setLoadedTags] = React.useState<string[]>([]);

  const [selectedSnapshotId, setSelectedSnapshotId] = React.useState<string | null>(null);

  const { data: snapshotsData } = useReportSnapshotsList();
  const snapshotOptions = React.useMemo(() => {
    const options: { label: string; value: string | null }[] = [
      { label: "Latest (auto)", value: null },
    ];
    if (snapshotsData?.items) {
      for (const snap of snapshotsData.items) {
        options.push({
          label: `Snapshot from ${formatSnapshotDate(snap.created_at)}`,
          value: snap.snapshot_id,
        });
      }
    }
    return options;
  }, [snapshotsData]);

  const selectedSnapshotOption = snapshotOptions.find((o) => o.value === selectedSnapshotId) ?? snapshotOptions[0];

  const report = useReport({
    snapshot_id: selectedSnapshotId ?? undefined,
    periods: ["daily", "weekly", "monthly", "all"],
    flow: state.flow,
    payee: appliedPayees.length > 0 ? appliedPayees : undefined,
    tags: appliedTags.length > 0 ? appliedTags : undefined,
  });

  React.useEffect(() => {
    if (report.data) {
      setLoadedPayees(prev => {
        const pSet = new Set<string>(prev);
        report.data.buckets.forEach((b: any) => {
          Object.values(b.periods).forEach((periodArray: any) => {
            periodArray?.forEach((p: any) => {
              p.metric?.transactions?.forEach((t: any) => {
                if (t.payee?.name) pSet.add(t.payee.name);
              });
            });
          });
        });
        return Array.from(pSet).sort();
      });

      setLoadedTags(prev => {
        const tSet = new Set<string>(prev);
        report.data.buckets.forEach((b: any) => {
          Object.values(b.periods).forEach((periodArray: any) => {
            periodArray?.forEach((p: any) => {
              p.metric?.transactions?.forEach((t: any) => {
                t.tags?.forEach((tag: any) => tSet.add(tag.name || tag));
              });
            });
          });
        });
        return Array.from(tSet).sort();
      });
    }
  }, [report.data]);

  const toggleFlow =
    React.useCallback(() => {
      setState((prev) => ({
        ...prev,

        flow:
          prev.flow ===
          "outflows"
            ? "inflows"
            : "outflows",

        selectedGroupKey:
          null,

        selectedPeriodId:
          null,
      }));
    }, []);

  const setFlow =
    React.useCallback(
      (
        flow: DashboardFlow
      ) => {
        setState((prev) => ({
          ...prev,

          flow,

          selectedGroupKey:
            null,

          selectedPeriodId:
            null,
        }));
      },
      []
    );

  const togglePeriodType =
    React.useCallback(() => {
      setState((prev) => ({
        ...prev,

        periodType:
          prev.periodType ===
          "rolling"
            ? "calendar"
            : "rolling",
      }));
    }, []);

  const toggleComparison =
    React.useCallback(() => {
      setState((prev) => ({
        ...prev,

        comparison:
          !prev.comparison,
      }));
    }, []);

  const setSelectedPeriodId =
    React.useCallback(
      (
        selectedPeriodId: DashboardState["selectedPeriodId"]
      ) => {
        setState((prev) => ({
          ...prev,

          selectedPeriodId,
        }));
      },
      []
    );

  const setSelectedGroupKey =
    React.useCallback(
      (
        selectedGroupKey: DashboardState["selectedGroupKey"]
      ) => {
        setState((prev) => ({
          ...prev,

          selectedGroupKey,
        }));
      },
      []
    );

  const stateSetters: DashboardStateSetters =
    React.useMemo(
      () => ({
        toggleFlow,

        setFlow,

        togglePeriodType,

        toggleComparison,

        setSelectedPeriodId,

        setSelectedGroupKey,
      }),
      [
        toggleFlow,
        setFlow,
        togglePeriodType,
        toggleComparison,
        setSelectedPeriodId,
        setSelectedGroupKey,
      ]
    );

  const isLoading = report.isLoading;
  const error = report.error;

  if (isLoading && !report.data) {
    return (
      <Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", height: "60vh" }}>
        <CircularProgress />
      </Box>
    );
  }

  if (error) {
    return (
      <Container sx={{ mt: 4 }}>
        <Alert severity="error">{String(error)}</Alert>
      </Container>
    );
  }

  if (!report.data) {
    return null;
  }

  const data = prepareReport(report.data);
  return (
    <Box>
      <Container>
        <Paper 
          sx={{ 
            mt: 4, 
            p: 2, 
            display: "flex", 
            flexDirection: { xs: "column", sm: "row" },
            gap: 2, 
            alignItems: { xs: "stretch", sm: "flex-end" }, 
            borderRadius: 4,
            mb: -2 // pull up to be closer to the dashboard container below
          }} 
          elevation={0} 
          variant="outlined"
        >
          <Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: { sm: 250 } }}>
            <Box sx={{ typography: 'caption', mb: 1, color: 'text.secondary' }}>
              Filter by Payee
            </Box>
            <Autocomplete
              multiple
              freeSolo
              options={loadedPayees}
              value={payeeInput}
              onChange={(_, val) => setPayeeInput(val as string[])}
              renderInput={(params) => <TextField {...params} placeholder="Add payees..." />}
              sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }}
            />
          </Box>
          <Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: { sm: 250 } }}>
            <Box sx={{ typography: 'caption', mb: 1, color: 'text.secondary' }}>
              Filter by Tags
            </Box>
            <Autocomplete
              multiple
              freeSolo
              options={loadedTags}
              value={tagsInput}
              onChange={(_, val) => setTagsInput(val as string[])}
              renderInput={(params) => <TextField {...params} placeholder="Add tags..." />}
              sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }}
            />
          </Box>
          <Box sx={{ display: 'flex', flexDirection: 'column', minWidth: { sm: 220 } }}>
            <Box sx={{ typography: 'caption', mb: 1, color: 'text.secondary' }}>
              Snapshot
            </Box>
            <Autocomplete
              options={snapshotOptions}
              value={selectedSnapshotOption}
              onChange={(_, option) => setSelectedSnapshotId(option?.value ?? null)}
              getOptionLabel={(o) => o.label}
              isOptionEqualToValue={(o, v) => o.value === v.value}
              renderInput={(params) => <TextField {...params} placeholder="Select snapshot..." />}
              sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }}
            />
          </Box>

          <Button
            variant="contained"
            size="large"
            onClick={() => {
              setAppliedPayees(payeeInput);
              setAppliedTags(tagsInput);
            }}
            disabled={isLoading}
            sx={{ height: 40, borderRadius: 2 }}
          >
            Apply
          </Button>
        </Paper>
      </Container>
      <DashboardView
        config={configuration}
        data={data}
        state={state}
        stateSetters={stateSetters}
        isFetching={report.isFetching}
      />
    </Box>
  );
}
