import { useCallback, useMemo, useState } from "react";
import {
  areDraftValuesEqual,
  getDraftValuesFromEntry,
  serializeChangedDraftToUpsertPayload,
  validateDraft,
} from "@/features/predictions/domain/prediction-serializers";
import type {
  PredictionCard,
  PredictionDraftValue,
  PredictionEntry,
  UpsertPredictionPayload,
} from "@/types/predictions";

type SaveHandler = (cardId: string, body: UpsertPredictionPayload) => Promise<PredictionEntry>;

type ControllerInput = {
  card: PredictionCard;
  entry?: PredictionEntry;
  onSave: SaveHandler;
  disabled?: boolean;
};

export type PredictionTileController = {
  draftValues: PredictionDraftValue[];
  persistedValues: PredictionDraftValue[];
  dirty: boolean;
  saving: boolean;
  errorMessage: string;
  successMessage: string;
  setText: (fieldId: string, value: string) => void;
  setNumber: (fieldId: string, value: number | null) => void;
  setDate: (fieldId: string, value: string) => void;
  submit: () => Promise<boolean>;
  resetFromEntry: (nextEntry?: PredictionEntry) => void;
};

function updateDraftValue(
  draftValues: PredictionDraftValue[],
  fieldId: string,
  updater: (current: PredictionDraftValue) => PredictionDraftValue,
) {
  return draftValues.map((value) => (value.fieldId === fieldId ? updater(value) : value));
}

export function usePredictionTileController({
  card,
  entry,
  onSave,
  disabled = false,
}: ControllerInput): PredictionTileController {
  const initialValues = useMemo(() => getDraftValuesFromEntry(card, entry), [card, entry]);

  const [draftValues, setDraftValues] = useState<PredictionDraftValue[]>(initialValues);
  const [persistedValues, setPersistedValues] = useState<PredictionDraftValue[]>(initialValues);
  const [saving, setSaving] = useState(false);
  const [errorMessage, setErrorMessage] = useState("");
  const [successMessage, setSuccessMessage] = useState("");

  const dirty = useMemo(
    () => !areDraftValuesEqual(draftValues, persistedValues),
    [draftValues, persistedValues],
  );

  const setText = useCallback((fieldId: string, value: string) => {
    setDraftValues((current) =>
      updateDraftValue(current, fieldId, (draft) => ({ ...draft, valueText: value })),
    );
  }, []);

  const setNumber = useCallback((fieldId: string, value: number | null) => {
    setDraftValues((current) =>
      updateDraftValue(current, fieldId, (draft) => ({ ...draft, valueNumber: value })),
    );
  }, []);

  const setDate = useCallback((fieldId: string, value: string) => {
    setDraftValues((current) =>
      updateDraftValue(current, fieldId, (draft) => ({ ...draft, valueDate: value })),
    );
  }, []);

  const submit = useCallback(async () => {
    if (disabled) {
      setErrorMessage("Le jeu est cloture. Les pronostics sont verrouilles.");
      return false;
    }

    const validation = validateDraft(card, draftValues);
    if (!validation.valid) {
      setErrorMessage(validation.message);
      setSuccessMessage("");
      return false;
    }

    const payload = serializeChangedDraftToUpsertPayload(card, draftValues, persistedValues);

    if (payload.values.length === 0) {
      setErrorMessage("");
      setSuccessMessage("Aucun changement a enregistrer");
      return true;
    }

    setSaving(true);
    setErrorMessage("");
    setSuccessMessage("");

    try {
      const updatedEntry = await onSave(card.id, payload);
      const nextValues = getDraftValuesFromEntry(card, updatedEntry);
      setDraftValues(nextValues);
      setPersistedValues(nextValues);
      setSuccessMessage("Pronostic enregistre");
      return true;
    } catch (error) {
      setErrorMessage(error instanceof Error ? error.message : "Erreur de sauvegarde");
      return false;
    } finally {
      setSaving(false);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [card, disabled, draftValues, persistedValues, onSave]);

  const resetFromEntry = useCallback((nextEntry?: PredictionEntry) => {
    const nextValues = getDraftValuesFromEntry(card, nextEntry);
    setDraftValues(nextValues);
    setPersistedValues(nextValues);
    setErrorMessage("");
    setSuccessMessage("");
  }, [card]);

  return useMemo(() => ({
    draftValues,
    persistedValues,
    dirty,
    saving,
    errorMessage,
    successMessage,
    setText,
    setNumber,
    setDate,
    submit,
    resetFromEntry,
  }), [draftValues, persistedValues, dirty, saving, errorMessage, successMessage, setText, setNumber, setDate, submit, resetFromEntry]);
}
