import * as React from "react";
import { useParams, useNavigate } from "react-router-dom";
import {
  Box,
  Container,
  Paper,
  Typography,
  Button,
  Chip,
  CircularProgress,
  Alert,
  Stepper,
  Step,
  StepLabel,
  StepIcon,
  LinearProgress,
  IconButton,
} from "@mui/material";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import ReplayIcon from "@mui/icons-material/Replay";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import ErrorIcon from "@mui/icons-material/Error";
import WarningAmberIcon from "@mui/icons-material/WarningAmber";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline";
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
import {
  useFetchRequest,
  useUpdateFetchRequest,
  useFetchRequestAmbiguities,
  useResolveAmbiguity,
} from "./features/fetch-requests";
import type {
  FetchRequestStatus,
  SSEEvent,
  ProgressMessage,
} from "./features/fetch-requests";
import { RETRY_MAX } from "./features/fetch-requests";
import { useConfig } from "../react-openapi";

const statusColors: Record<FetchRequestStatus, "default" | "primary" | "warning" | "info" | "success" | "error"> = {
  pending: "default",
  processing: "info",
  paused: "warning",
  raw_expenses_done: "primary",
  enriched_done: "warning",
  completed: "success",
  failed: "error",
};

const statusIcons: Record<FetchRequestStatus, React.ReactNode> = {
  pending: <PlayArrowIcon sx={{ fontSize: 16 }} />,
  processing: <CircularProgress size={14} />,
  paused: <WarningAmberIcon sx={{ fontSize: 16 }} />,
  raw_expenses_done: <CheckCircleIcon sx={{ fontSize: 16 }} />,
  enriched_done: <CheckCircleIcon sx={{ fontSize: 16 }} />,
  completed: <CheckCircleIcon sx={{ fontSize: 16 }} />,
  failed: <ErrorIcon sx={{ fontSize: 16 }} />,
};

function computeProgressPercent(
  status: FetchRequestStatus,
  liveCount: number,
  seenSteps: Set<string>,
): number {
  if (status === "pending") return 0;
  if (status === "completed") return 100;

  if (seenSteps.has("complete")) return 90;
  if (seenSteps.has("enrich")) return 85;
  if (seenSteps.has("txn_dicts/completed")) return 80;

  if (seenSteps.has("txn_dicts/started") && liveCount > 0) {
    return Math.min(80, 35 + Math.min(liveCount, 300) / 300 * 45);
  }

  if (seenSteps.has("txn_blocks")) return 35;
  if (seenSteps.has("raw_lines")) return 25;
  if (seenSteps.has("load_content")) return 15;

  return status === "processing" || status === "paused" ? 10 : 0;
}

const stepLabels = ["Load Content", "Extract", "Raw Expense", "Enrich", "Save"];

function computeActiveStep(status: FetchRequestStatus, seenSteps: Set<string>): number {
  if (status === "completed") return stepLabels.length;
  if (seenSteps.has("complete")) return 4;
  if (seenSteps.has("enrich") || status === "enriched_done") return 3;
  if (seenSteps.has("txn_dicts") || status === "raw_expenses_done") return 2;
  if (seenSteps.has("txn_blocks")) return 1;
  if (seenSteps.has("load_content") || status === "processing" || status === "paused") return 0;
  return -1;
}

function formatProgressMessage(msg: ProgressMessage): string {
  if (msg.lines !== undefined) return `${msg.lines} lines`;
  if (msg.blocks !== undefined) return `${msg.blocks} blocks`;
  if (msg.count !== undefined && msg.unit) return `${msg.count} ${msg.unit}`;
  if (msg.count !== undefined) return `${msg.count} items`;
  if (msg.raw_ocr_line) return `"${msg.raw_ocr_line.slice(0, 60)}${msg.raw_ocr_line.length > 60 ? "…" : ""}"`;
  return "";
}

function sseIcon(status: SSEEvent["status"]) {
  switch (status) {
    case "started": return <CircularProgress size={14} />;
    case "completed": return <CheckCircleIcon sx={{ fontSize: 16, color: "success.main" }} />;
    case "skipped": return <RemoveCircleOutlineIcon sx={{ fontSize: 16, color: "text.disabled" }} />;
    case "paused": return <WarningAmberIcon sx={{ fontSize: 16, color: "warning.main" }} />;
    case "progress": return (
      <FiberManualRecordIcon
        sx={{ fontSize: 14, color: "info.main" }}
      />
    );
  }
}

function isMathValid(candidate: { amount: number; balance: number }, prevBalance: number) {
  return (
    candidate.balance === prevBalance + candidate.amount ||
    candidate.balance === prevBalance - candidate.amount ||
    Math.abs(candidate.balance - (prevBalance + candidate.amount)) < 0.01 ||
    Math.abs(candidate.balance - (prevBalance - candidate.amount)) < 0.01
  );
}

export default function FetchRequestDetail() {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const config = useConfig();

  const { data: fetchRequest, isLoading, error: fetchError, refetch: refetchRequest } = useFetchRequest(id!);
  const updateMutation = useUpdateFetchRequest();
  const resolveMutation = useResolveAmbiguity();
  const { data: ambiguities, refetch: refetchAmbiguities } = useFetchRequestAmbiguities(id!);

  const [sseEvents, setSseEvents] = React.useState<SSEEvent[]>([]);
  const [sseConnected, setSseConnected] = React.useState(false);
  const [liveParsedCount, setLiveParsedCount] = React.useState<number>(0);
  const [stepStats, setStepStats] = React.useState<Record<string, number>>({});
  const sseRef = React.useRef<EventSource | null>(null);
  const feedRef = React.useRef<HTMLDivElement>(null);

  const stepMessages = React.useMemo(() => {
    const msgs: Record<number, string> = {};
    const source = (fetchRequest as any)?.source;

    const rawLineCount = stepStats.raw_lines ?? (source?.raw_lines?.length ?? 0);
    if (rawLineCount) msgs[0] = `${rawLineCount} raw lines`;

    const blockCount = stepStats.txn_blocks ?? 0;
    if (blockCount) msgs[1] = `${blockCount} blocks`;

    const dictCount = stepStats.txn_dicts ?? liveParsedCount ?? 0;
    if (dictCount) msgs[2] = `${dictCount} dicts`;

    if (["enriched_done", "completed"].includes((fetchRequest as any)?.status))
      msgs[3] = "done";
    if ((fetchRequest as any)?.status === "completed")
      msgs[4] = (fetchRequest as any)?.completed_at
        ? new Date((fetchRequest as any).completed_at).toLocaleString() : "done";

    return msgs;
  }, [fetchRequest, stepStats, liveParsedCount]);

  React.useEffect(() => {
    if (!id || !config?.baseUrl) return;
    const url = `${config.baseUrl}/fetch-requests/${id}/events`;
    const es = new EventSource(url);
    sseRef.current = es;

    es.onopen = () => setSseConnected(true);
    es.onerror = () => setSseConnected(false);
    es.onmessage = (event) => {
      try {
        const parsed: SSEEvent = JSON.parse(event.data);
        setSseEvents((prev) => [...prev, parsed]);

        if (parsed.step === "txn_dicts" && parsed.status === "progress" && parsed.message.count !== undefined) {
          setLiveParsedCount(parsed.message.count);
        }

        if (parsed.status === "completed") {
          const stats: Record<string, number> = {};
          if (parsed.step === "raw_lines" && parsed.message.lines !== undefined) stats.raw_lines = parsed.message.lines;
          if (parsed.step === "txn_blocks" && parsed.message.blocks !== undefined) stats.txn_blocks = parsed.message.blocks;
          if (parsed.step === "txn_dicts" && parsed.message.count !== undefined) stats.txn_dicts = parsed.message.count;
          if (Object.keys(stats).length) {
            setStepStats((prev) => ({ ...prev, ...stats }));
          }
        }

        if (parsed.status === "paused") {
          refetchRequest();
          refetchAmbiguities();
        }
        if (parsed.status === "completed" || parsed.step === "resume_extract") {
          refetchRequest();
        }
      } catch {
        // ignore malformed events
      }
    };

    return () => {
      es.close();
      sseRef.current = null;
    };
  }, [id, config?.baseUrl]);

  React.useEffect(() => {
    if (feedRef.current) {
      feedRef.current.scrollTop = feedRef.current.scrollHeight;
    }
  }, [sseEvents]);

  const displayEvents = React.useMemo(() => {
    let lastProgressIdx = -1;
    for (let i = sseEvents.length - 1; i >= 0; i--) {
      if (sseEvents[i].step === "txn_dicts" && sseEvents[i].status === "progress") {
        lastProgressIdx = i;
        break;
      }
    }

    const terminalStatuses = new Set(["completed", "skipped", "paused"]);
    return sseEvents.filter((e, i) => {
      if (e.step === "txn_dicts" && e.status === "progress") return i === lastProgressIdx;
      if (e.status === "started") {
        return !sseEvents.slice(i + 1).some(
          (later) => later.step === e.step && terminalStatuses.has(later.status),
        );
      }
      return true;
    });
  }, [sseEvents]);

  const seenSteps = React.useMemo(() => {
    const steps = new Set<string>();
    for (const evt of sseEvents) {
      steps.add(evt.step);
      if (evt.status === "completed") steps.add(`${evt.step}/completed`);
      if (evt.status === "started") steps.add(`${evt.step}/started`);
      if (evt.status === "progress") steps.add(`${evt.step}/progress`);
    }
    return steps;
  }, [sseEvents]);

  const progressPercent = React.useMemo(
    () => computeProgressPercent(
      (fetchRequest as any)?.status as FetchRequestStatus ?? "pending",
      liveParsedCount,
      seenSteps,
    ),
    [fetchRequest, liveParsedCount, seenSteps],
  );

  const displayParsedCount = React.useMemo(() => {
    if (liveParsedCount > 0) return liveParsedCount;
    const source = (fetchRequest as any)?.source;
    const persistedCount = source?.txn_dict_count ?? source?.txn_dicts_count ?? 0;
    if (persistedCount > 0) return persistedCount;
    const dicts = source?.txn_dicts;
    if (Array.isArray(dicts) && dicts.length > 0) return dicts.length;
    return 0;
  }, [liveParsedCount, fetchRequest]);

  const handleRetry = async () => {
    if (!id) return;
    try {
      await updateMutation.mutateAsync({ id, data: { status: "pending" } });
    } catch {
      // handled by react query
    }
  };

  const handleResolve = async (ambiguity: any, candidate: { amount: number; balance: number }) => {
    await resolveMutation.mutateAsync({
      ambiguityId: ambiguity.id,
      payload: { chosen: { amount: candidate.amount, balance: candidate.balance } },
    });
    refetchAmbiguities();
  };

  if (isLoading) {
    return (
      <Box sx={{ display: "flex", justifyContent: "center", p: 8 }}>
        <CircularProgress />
      </Box>
    );
  }

  if (fetchError || !fetchRequest) {
    return (
      <Container sx={{ mt: 4 }}>
        <Button startIcon={<ArrowBackIcon />} onClick={() => navigate("/fetch-requests")} sx={{ mb: 2 }}>
          Back
        </Button>
        <Alert severity="error">Failed to load fetch request</Alert>
      </Container>
    );
  }

  const req = fetchRequest as any;
  const activeStep = computeActiveStep(req.status as FetchRequestStatus, seenSteps);
  const retryCount = req.retry_count ?? 0;
  const isRetryExhausted = retryCount >= RETRY_MAX;
  const pendingAmbiguities = ambiguities?.filter((a: any) => a.status === "pending") ?? [];
  const resolvedAmbiguities = ambiguities?.filter((a: any) => a.status === "resolved") ?? [];
  const hasAmbiguities = ambiguities && ambiguities.length > 0;
  const allResolved = hasAmbiguities && pendingAmbiguities.length === 0;
  const ambiguitiesLoading = !ambiguities;

  return (
    <Container sx={{ mt: 4, mb: 4 }}>
      <Button startIcon={<ArrowBackIcon />} onClick={() => navigate("/fetch-requests")} sx={{ mb: 2 }}>
        Back to Fetch Requests
      </Button>

      <Paper sx={{ p: 3, borderRadius: 4, mb: 3 }} variant="outlined">
        <Box sx={{ display: "flex", alignItems: "center", gap: 2, mb: 2, flexWrap: "wrap" }}>
          <Chip
            icon={statusIcons[req.status as FetchRequestStatus] as any}
            label={req.status.replace(/_/g, " ")}
            color={statusColors[req.status as FetchRequestStatus]}
          />
          <Typography variant="h6" fontWeight={600}>{req.account_name}</Typography>
          <Chip
            label={"path" in req.source ? "File" : "Email"}
            size="small"
            variant="outlined"
            color={"path" in req.source ? "primary" : "secondary"}
          />
        </Box>

        <Box sx={{ display: "flex", gap: 4, flexWrap: "wrap", mb: 2 }}>
          <Box>
            <Typography variant="caption" color="text.secondary">Date Range</Typography>
            <Typography variant="body2">
              {(req as any).start_date ? new Date((req as any).start_date).toLocaleDateString() : "?"} → {(req as any).end_date ? new Date((req as any).end_date).toLocaleDateString() : "?"}
            </Typography>
          </Box>
          <Box>
            <Typography variant="caption" color="text.secondary">Created</Typography>
            <Typography variant="body2">{new Date(req.created_at).toLocaleString()}</Typography>
          </Box>
          {req.completed_at && (
            <Box>
              <Typography variant="caption" color="text.secondary">Completed</Typography>
              <Typography variant="body2">{new Date(req.completed_at).toLocaleString()}</Typography>
            </Box>
          )}
        </Box>

        <Box sx={{ mb: 2 }}>
          <Box sx={{ display: "flex", alignItems: "center", gap: 2, mb: 0.5 }}>
            <Typography variant="caption" color="text.secondary">
              Overall Progress
            </Typography>
            {["processing", "paused"].includes(req.status) && displayParsedCount > 0 && (
              <Typography variant="caption" fontWeight={600} color="info.main">
                Validated: {displayParsedCount} transactions
              </Typography>
            )}
          </Box>
          <LinearProgress
            variant="determinate"
            value={progressPercent}
            color={req.status === "failed" ? "error" : req.status === "completed" ? "success" : "primary"}
            sx={{ borderRadius: 1, height: 8, transition: "width 0.3s ease" }}
          />
          <Typography variant="caption" color="text.secondary" sx={{ mt: 0.25, display: "block" }}>
            {progressPercent}%
          </Typography>
        </Box>

        <Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
          <Box sx={{ flex: 1, maxWidth: 300 }}>
            <Typography variant="caption" color="text.secondary">
              Retries: {retryCount}/{RETRY_MAX}
            </Typography>
            <LinearProgress
              variant="determinate"
              value={(retryCount / RETRY_MAX) * 100}
              color={isRetryExhausted ? "error" : "primary"}
              sx={{ mt: 0.5, borderRadius: 1, height: 6 }}
            />
          </Box>
          {req.status === "failed" && !isRetryExhausted && (
            <Button
              variant="outlined"
              size="small"
              startIcon={<ReplayIcon />}
              onClick={handleRetry}
              disabled={updateMutation.isPending}
            >
              Retry
            </Button>
          )}
        </Box>
      </Paper>

      {req.status === "failed" && req.error_message && (
        <Alert severity="error" sx={{ mb: 3, borderRadius: 2 }}>
          {req.error_message}
        </Alert>
      )}

      {isRetryExhausted && req.status === "failed" && (
        <Alert severity="info" sx={{ mb: 3, borderRadius: 2 }}>
          Max retries reached — no further retry attempts will be made.
        </Alert>
      )}

      <Paper sx={{ p: 3, borderRadius: 4, mb: 3 }} variant="outlined">
        <Typography variant="subtitle1" fontWeight={600} gutterBottom>
          Pipeline Progress
        </Typography>
        <Stepper activeStep={activeStep} alternativeLabel>
          {stepLabels.map((label, index) => {
            const isCompleted = index < activeStep;
            const isActive = index === activeStep;
            const isPaused = req.status === "paused" && isActive;
            const isFailed = req.status === "failed" && isActive;

            let icon: React.ReactNode;
            if (isCompleted) {
              icon = <CheckCircleIcon sx={{ color: "success.main" }} />;
            } else if (isFailed) {
              icon = <ErrorIcon sx={{ color: "error.main" }} />;
            } else if (isPaused) {
              icon = <WarningAmberIcon sx={{ color: "warning.main" }} />;
            } else if (isActive) {
              icon = <CircularProgress size={20} />;
            } else {
              icon = <Typography variant="caption" color="text.disabled">{index + 1}</Typography>;
            }

            const stepMsg = stepMessages[index];

            return (
              <Step key={label}>
                <StepLabel
                  StepIconComponent={() => <Box sx={{ display: "flex", alignItems: "center" }}>{icon}</Box>}
                >
                  <Typography variant="body2" fontWeight={600}>{label}</Typography>
                  {stepMsg && (
                    <Typography variant="caption" color="text.secondary" sx={{ display: "block", lineHeight: 1.2 }}>
                      {stepMsg}
                    </Typography>
                  )}
                </StepLabel>
              </Step>
            );
          })}
        </Stepper>
      </Paper>

      <Paper sx={{ borderRadius: 4, mb: 3 }} variant="outlined">
        <Box sx={{ display: "flex", alignItems: "center", gap: 1, p: 2, pb: 0 }}>
          <Typography variant="subtitle1" fontWeight={600} sx={{ flex: 1 }}>
            Progress Events
          </Typography>
          <Box
            sx={{
              width: 10,
              height: 10,
              borderRadius: "50%",
              bgcolor: sseConnected ? "success.main" : "error.main",
              flexShrink: 0,
            }}
          />
          <Typography variant="caption" color="text.secondary">
            {sseConnected ? "Connected" : "Disconnected"}
          </Typography>
        </Box>
        <Box
          ref={feedRef}
          sx={{
            maxHeight: 300,
            overflowY: "auto",
            p: 2,
            display: "flex",
            flexDirection: "column",
            gap: 1,
          }}
        >
          {displayEvents.length === 0 ? (
            <Typography variant="body2" color="text.disabled" sx={{ textAlign: "center", py: 2 }}>
              Waiting for events...
            </Typography>
          ) : (
            displayEvents.map((evt, i) => (
              <Box
                key={i}
                sx={{
                  display: "flex",
                  alignItems: "center",
                  gap: 1.5,
                  p: 1,
                  borderRadius: 2,
                  bgcolor: "action.hover",
                }}
              >
                {sseIcon(evt.status)}
                <Box sx={{ flex: 1 }}>
                  <Typography variant="body2" fontWeight={600}>
                    {evt.step.replace(/_/g, " ")}
                  </Typography>
                  {evt.message && formatProgressMessage(evt.message) && (
                    <Typography variant="caption" color="text.secondary">
                      {formatProgressMessage(evt.message)}
                    </Typography>
                  )}
                </Box>
                <Typography variant="caption" color="text.disabled">
                  {new Date().toLocaleTimeString()}
                </Typography>
              </Box>
            ))
          )}
        </Box>
      </Paper>

      {hasAmbiguities && (
        <Paper sx={{ p: 3, borderRadius: 4, mb: 3 }} variant="outlined">
          <Typography variant="subtitle1" fontWeight={600} gutterBottom>
            Ambiguity Resolution
          </Typography>

          {allResolved ? (
            <Alert severity="success" sx={{ mb: 2, borderRadius: 2 }}>
              All ambiguities resolved — pipeline will resume on next poll cycle
            </Alert>
          ) : (
            <Alert severity="warning" sx={{ mb: 2, borderRadius: 2 }}>
              Pipeline paused — resolve ambiguities to continue
            </Alert>
          )}

          <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
            {ambiguities.map((ambiguity: any) => {
              const isResolved = ambiguity.status === "resolved";
              return (
                <Paper
                  key={ambiguity.id}
                  sx={{
                    p: 2,
                    borderRadius: 3,
                    border: 1,
                    borderColor: isResolved ? "success.main" : "divider",
                    opacity: isResolved ? 0.8 : 1,
                  }}
                  variant="outlined"
                >
                  <Box sx={{ fontFamily: "monospace", fontSize: "0.85rem", mb: 1.5, p: 1, bgcolor: "grey.900", borderRadius: 1, color: "grey.100" }}>
                    {ambiguity.line}
                  </Box>

                  <Box sx={{ display: "flex", gap: 3, mb: 1.5, flexWrap: "wrap" }}>
                    <Box>
                      <Typography variant="caption" color="text.secondary">OCR Amount</Typography>
                      <Typography variant="body2" sx={{ textDecoration: "line-through", color: "text.secondary" }}>
                        ₹{ambiguity.ocr_amount}
                      </Typography>
                    </Box>
                    <Box>
                      <Typography variant="caption" color="text.secondary">OCR Balance</Typography>
                      <Typography variant="body2" sx={{ textDecoration: "line-through", color: "text.secondary" }}>
                        ₹{ambiguity.ocr_balance}
                      </Typography>
                    </Box>
                    <Box>
                      <Typography variant="caption" color="text.secondary">Previous Balance</Typography>
                      <Typography variant="body2">₹{ambiguity.prev_balance}</Typography>
                    </Box>
                  </Box>

                  {isResolved ? (
                    <Alert severity="success" sx={{ py: 0.5, borderRadius: 2 }} icon={<CheckCircleIcon />}>
                      Resolved: ₹{ambiguity.chosen?.amount} / ₹{ambiguity.chosen?.balance}
                    </Alert>
                  ) : (
                    <Box sx={{ display: "flex", gap: 1, flexWrap: "wrap" }}>
                      {ambiguity.candidates.map((candidate: any, ci: number) => {
                        const isCredit = candidate.amount > 0;
                        const isDebit = candidate.amount < 0;
                        const cColor = isCredit ? "success.main" : isDebit ? "error.main" : undefined;
                        return (
                          <Button
                            key={ci}
                            variant="outlined"
                            size="small"
                            onClick={() => handleResolve(ambiguity, candidate)}
                            disabled={resolveMutation.isPending}
                            sx={{
                              borderColor: cColor,
                              color: cColor,
                              "&:hover": cColor ? { borderColor: cColor } : undefined,
                            }}
                          >
                            ₹{candidate.amount} / ₹{candidate.balance}
                          </Button>
                        );
                      })}
                    </Box>
                  )}
                </Paper>
              );
            })}
          </Box>
      </Paper>
    )}
    </Container>
  );
}
