import { useCallback, useEffect, useState } from 'react';
import axios from 'axios';
import { t } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { useToast } from '../components/shared/ToastProvider';
import { extractErrorMessage } from '../utils/errorHandler';
import type { ClassSession, ResolverReport, ApproveWeekResult } from '../types/class';

export interface UseClassSessionsArgs {
  berId: number | null;
  from: string;
  to: string;
  enabled: boolean;
}

export interface UseClassSessionsReturn {
  sessions: ClassSession[];
  loading: boolean;
  error: string;
  reload: () => Promise<void>;
  resolveWeek: (year: number, weekIso: number) => Promise<ResolverReport>;
  approveWeek: (year: number, weekIso: number) => Promise<ApproveWeekResult>;
  discardProposals: (year: number, weekIso: number) => Promise<number>;
  approveSession: (id: number) => Promise<void>;
  rejectProposal: (id: number) => Promise<void>;
  overrideProposedUser: (id: number, userId: number) => Promise<void>;
  cancelSession: (id: number) => Promise<void>;
  manualAssign: (id: number, userId: number) => Promise<void>;
}

const EMPTY_REPORT: ResolverReport = {
  sessions_proposed: 0,
  sessions_unstaffed: 0,
  sessions_skipped_approved: 0,
};

const EMPTY_APPROVE: ApproveWeekResult = { approved: 0, failed: [] };

function isoWeekStart(year: number, weekIso: number): Date {
  const simple: Date = new Date(Date.UTC(year, 0, 1 + (weekIso - 1) * 7));
  const dow: number = simple.getUTCDay();
  const diff: number = dow <= 4 ? dow - 1 : dow - 8;
  simple.setUTCDate(simple.getUTCDate() - diff);
  return simple;
}

function toIsoDate(d: Date): string {
  const y: number = d.getUTCFullYear();
  const m: string = String(d.getUTCMonth() + 1).padStart(2, '0');
  const day: string = String(d.getUTCDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}

export function useClassSessions(args: UseClassSessionsArgs): UseClassSessionsReturn {
  const { berId, from, to, enabled } = args;
  const { i18n } = useLingui();
  const toast = useToast();
  const [sessions, setSessions] = useState<ClassSession[]>([]);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string>('');

  const reload = useCallback(async (): Promise<void> => {
    if (!enabled || berId === null) {
      setSessions([]);
      setError('');
      return;
    }
    try {
      setLoading(true);
      setError('');
      const response = await axios.get('/zeiterfassung/class-sessions', {
        params: { ber_id: berId, from, to },
      });
      setSessions(Array.isArray(response.data) ? (response.data as ClassSession[]) : []);
    } catch (err) {
      const message: string = extractErrorMessage(err);
      setError(message);
      setSessions([]);
    } finally {
      setLoading(false);
    }
  }, [enabled, berId, from, to]);

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

  const resolveWeek = useCallback(
    async (year: number, weekIso: number): Promise<ResolverReport> => {
      if (!enabled || berId === null) return EMPTY_REPORT;
      try {
        const response = await axios.post('/zeiterfassung/class-sessions/resolve-week', {
          ber_id: berId,
          year,
          week_iso: weekIso,
        });
        const report: ResolverReport = (response.data as ResolverReport) ?? EMPTY_REPORT;
        toast.success({
          title: i18n._(t`Klassen zugewiesen`),
          description: i18n._(
            t`${report.sessions_proposed} Vorschläge, ${report.sessions_unstaffed} unbesetzt`
          ),
        });
        await reload();
        return report;
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler bei der Klassenzuweisung`), description: message });
        throw err;
      }
    },
    [enabled, berId, i18n, reload, toast]
  );

  const approveWeek = useCallback(
    async (year: number, weekIso: number): Promise<ApproveWeekResult> => {
      if (!enabled || berId === null) return EMPTY_APPROVE;
      try {
        const response = await axios.post('/zeiterfassung/class-sessions/approve-week', {
          ber_id: berId,
          year,
          week_iso: weekIso,
        });
        const result: ApproveWeekResult = (response.data as ApproveWeekResult) ?? EMPTY_APPROVE;
        toast.success({
          title: i18n._(t`Klassen bestätigt`),
          description: i18n._(t`${result.approved} bestätigt`),
        });
        await reload();
        return result;
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler bei der Bestätigung`), description: message });
        throw err;
      }
    },
    [enabled, berId, i18n, reload, toast]
  );

  const discardProposals = useCallback(
    async (year: number, weekIso: number): Promise<number> => {
      if (!enabled || berId === null) return 0;
      const weekStart: Date = isoWeekStart(year, weekIso);
      const weekEnd: Date = new Date(weekStart);
      weekEnd.setUTCDate(weekEnd.getUTCDate() + 6);
      const fromIso: string = toIsoDate(weekStart);
      const toIso: string = toIsoDate(weekEnd);
      let processed: number = 0;
      try {
        const list = await axios.get('/zeiterfassung/class-sessions', {
          params: { ber_id: berId, from: fromIso, to: toIso, 'status[]': 'proposed' },
        });
        const candidates: ClassSession[] = Array.isArray(list.data)
          ? (list.data as ClassSession[]).filter((s) => s.zcs_status === 'proposed')
          : [];
        for (const s of candidates) {
          try {
            await axios.post(`/zeiterfassung/class-sessions/${s.zcs_id}/reject-proposal`);
            processed += 1;
          } catch {
            // continue; best-effort
          }
        }
        toast.success({
          title: i18n._(t`Vorschläge verworfen`),
          description: i18n._(t`${processed} zurückgesetzt`),
        });
        await reload();
        return processed;
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler beim Verwerfen`), description: message });
        throw err;
      }
    },
    [enabled, berId, i18n, reload, toast]
  );

  const approveSession = useCallback(
    async (id: number): Promise<void> => {
      if (!enabled) return;
      try {
        await axios.post(`/zeiterfassung/class-sessions/${id}/approve`);
        toast.success({ title: i18n._(t`Sitzung bestätigt`) });
        await reload();
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler bei der Bestätigung`), description: message });
        throw err;
      }
    },
    [enabled, i18n, reload, toast]
  );

  const rejectProposal = useCallback(
    async (id: number): Promise<void> => {
      if (!enabled) return;
      try {
        await axios.post(`/zeiterfassung/class-sessions/${id}/reject-proposal`);
        toast.success({ title: i18n._(t`Vorschlag verworfen`) });
        await reload();
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler beim Verwerfen`), description: message });
        throw err;
      }
    },
    [enabled, i18n, reload, toast]
  );

  const overrideProposedUser = useCallback(
    async (id: number, userId: number): Promise<void> => {
      if (!enabled) return;
      try {
        await axios.post(`/zeiterfassung/class-sessions/${id}/override-proposed-user`, {
          proposed_user_id: userId,
        });
        toast.success({ title: i18n._(t`Vorgeschlagener Mitarbeiter geändert`) });
        await reload();
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler beim Überschreiben`), description: message });
        throw err;
      }
    },
    [enabled, i18n, reload, toast]
  );

  const cancelSession = useCallback(
    async (id: number): Promise<void> => {
      if (!enabled) return;
      try {
        await axios.post(`/zeiterfassung/class-sessions/${id}/cancel`);
        toast.success({ title: i18n._(t`Sitzung abgesagt`) });
        await reload();
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler beim Absagen`), description: message });
        throw err;
      }
    },
    [enabled, i18n, reload, toast]
  );

  const manualAssign = useCallback(
    async (id: number, userId: number): Promise<void> => {
      if (!enabled) return;
      try {
        await axios.post(`/zeiterfassung/class-sessions/${id}/manual-assign`, {
          user_id: userId,
        });
        toast.success({ title: i18n._(t`Manuell zugewiesen`) });
        await reload();
      } catch (err) {
        const message: string = extractErrorMessage(err);
        toast.error({ title: i18n._(t`Fehler bei der Zuweisung`), description: message });
        throw err;
      }
    },
    [enabled, i18n, reload, toast]
  );

  return {
    sessions,
    loading,
    error,
    reload,
    resolveWeek,
    approveWeek,
    discardProposals,
    approveSession,
    rejectProposal,
    overrideProposedUser,
    cancelSession,
    manualAssign,
  };
}

export default useClassSessions;
