"use client";

import { useCallback, useState } from "react";

import {
  meetingRequestService,
  type ApiGovernmentAgency,
  type ApiIssue,
  type ApiCategory,
  type ApiMeetingRequest,
  type ApiWorkingGroup,
  type CreateMeetingRequestPayload,
  type CreateWorkingGroupIssuePayload,
  type UpdateMeetingRequestPayload,
} from "../service/meeting-request-service";

type UnknownRecord = Record<string, unknown>;

function isRecord(value: unknown): value is UnknownRecord {
  return typeof value === "object" && value !== null;
}

function isApiMeetingRequest(value: unknown): value is ApiMeetingRequest {
  if (!isRecord(value)) return false;

  return typeof value.id === "number" && Number.isFinite(value.id);
}

function extractMeetingRequestFromSendResponse(
  value: unknown,
): ApiMeetingRequest | null {
  if (isApiMeetingRequest(value)) {
    return value;
  }

  if (!isRecord(value)) {
    return null;
  }

  const firstData = value.data;

  if (isApiMeetingRequest(firstData)) {
    return firstData;
  }

  if (isRecord(firstData)) {
    const nestedData = firstData.data;

    if (isApiMeetingRequest(nestedData)) {
      return nestedData;
    }
  }

  return null;
}

function keepValidMeetingRequests(
  items: ApiMeetingRequest[],
): ApiMeetingRequest[] {
  return items.filter((item) => isApiMeetingRequest(item));
}

export function useMeetingRequests() {
  const [data, setData] = useState<ApiMeetingRequest[]>([]);
  const [selected, setSelected] = useState<ApiMeetingRequest | null>(null);

  const [agencies, setAgencies] = useState<ApiGovernmentAgency[]>([]);
  const [workingGroups, setWorkingGroups] = useState<ApiWorkingGroup[]>([]);
  const [issues, setIssues] = useState<ApiIssue[]>([]);
  const [categories, setCategories] = useState<ApiCategory[]>(
    [],
  );

  const [loading, setLoading] = useState<boolean>(false);
  const [detailLoading, setDetailLoading] = useState<boolean>(false);
  const [loadingAgencies, setLoadingAgencies] = useState<boolean>(false);
  const [loadingWorkingGroups, setLoadingWorkingGroups] =
    useState<boolean>(false);
  const [loadingIssues, setLoadingIssues] = useState<boolean>(false);
  const [loadingCategories, setLoadingCategories] =
    useState<boolean>(false);

  const [saving, setSaving] = useState<boolean>(false);
  const [deleting, setDeleting] = useState<boolean>(false);
  const [sending, setSending] = useState<boolean>(false);

  const [error, setError] = useState<string | null>(null);
  // Sending has its own error so a failed send never replaces the meeting
  // request list with an error message.
  const [sendError, setSendError] = useState<string | null>(null);
  const [agencyError, setAgencyError] = useState<string | null>(null);
  const [workingGroupError, setWorkingGroupError] = useState<string | null>(
    null,
  );
  const [issueError, setIssueError] = useState<string | null>(null);

  const fetchGovernmentAgencies = useCallback(async () => {
    try {
      setLoadingAgencies(true);
      setAgencyError(null);

      const result = await meetingRequestService.getGovernmentAgencies();
      const safeResult = Array.isArray(result) ? result : [];

      setAgencies(safeResult);

      return safeResult;
    } catch (err: unknown) {
      const message =
        err instanceof Error
          ? err.message
          : "Failed to fetch government agencies";

      setAgencyError(message);
      setAgencies([]);

      return [];
    } finally {
      setLoadingAgencies(false);
    }
  }, []);

  const fetchWorkingGroups = useCallback(async () => {
    try {
      setLoadingWorkingGroups(true);
      setWorkingGroupError(null);

      const result = await meetingRequestService.getWorkingGroups();
      const safeResult = Array.isArray(result) ? result : [];

      setWorkingGroups(safeResult);

      return safeResult;
    } catch (err: unknown) {
      const message =
        err instanceof Error ? err.message : "Failed to fetch working groups";

      setWorkingGroupError(message);
      setWorkingGroups([]);

      return [];
    } finally {
      setLoadingWorkingGroups(false);
    }
  }, []);

  const fetchWorkingGroupIssues = useCallback(async () => {
    try {
      setLoadingIssues(true);
      setIssueError(null);

      const result = await meetingRequestService.getWorkingGroupIssues();
      const safeResult = Array.isArray(result) ? result : [];

      setIssues(safeResult);

      return safeResult;
    } catch (err: unknown) {
      const message =
        err instanceof Error ? err.message : "Failed to fetch issues";

      setIssueError(message);
      setIssues([]);

      return [];
    } finally {
      setLoadingIssues(false);
    }
  }, []);

  const fetchCategories = useCallback(async () => {
    try {
      setLoadingCategories(true);
      setIssueError(null);

      const result = await meetingRequestService.getCategories();
      const safeResult = Array.isArray(result) ? result : [];

      setCategories(safeResult);

      return safeResult;
    } catch {
      setCategories([]);
      return [];
    } finally {
      setLoadingCategories(false);
    }
  }, []);

  const createWorkingGroupIssue = useCallback(
    async (payload: CreateWorkingGroupIssuePayload) => {
      try {
        setSaving(true);
        setIssueError(null);

        const result =
          await meetingRequestService.createWorkingGroupIssue(payload);

        setIssues((current: ApiIssue[]) => [result, ...current]);

        return result;
      } catch (err: unknown) {
        const message =
          err instanceof Error ? err.message : "Failed to create issue";

        setIssueError(message);

        return null;
      } finally {
        setSaving(false);
      }
    },
    [],
  );

  const fetchMeetingRequests = useCallback(async () => {
    try {
      setLoading(true);
      setError(null);

      const result = await meetingRequestService.getAll();
      const safeResult = Array.isArray(result)
        ? keepValidMeetingRequests(result)
        : [];

      setData(safeResult);

      return safeResult;
    } catch (err: unknown) {
      const message =
        err instanceof Error
          ? err.message
          : "Failed to fetch meeting requests";

      setError(message);
      setData([]);

      return [];
    } finally {
      setLoading(false);
    }
  }, []);

  const fetchMeetingRequestById = useCallback(async (id: number) => {
    try {
      setDetailLoading(true);
      setError(null);

      const result = await meetingRequestService.getById(id);

      setSelected(result);

      return result;
    } catch (err: unknown) {
      const message =
        err instanceof Error
          ? err.message
          : "Failed to fetch meeting request";

      setError(message);
      setSelected(null);

      return null;
    } finally {
      setDetailLoading(false);
    }
  }, []);

  const createMeetingRequest = useCallback(
    async (payload: CreateMeetingRequestPayload) => {
      try {
        setSaving(true);
        setError(null);

        const result = await meetingRequestService.create(payload);

        setData((current: ApiMeetingRequest[]) => {
          if (!isApiMeetingRequest(result)) {
            return current;
          }

          return [result, ...current];
        });

        return result;
      } catch (err: unknown) {
        const message =
          err instanceof Error
            ? err.message
            : "Failed to create meeting request";

        setError(message);

        return null;
      } finally {
        setSaving(false);
      }
    },
    [],
  );

  const updateMeetingRequest = useCallback(
    async (id: number, payload: UpdateMeetingRequestPayload) => {
      try {
        setSaving(true);
        setError(null);

        const result = await meetingRequestService.update(id, payload);

        if (!isApiMeetingRequest(result)) {
          throw new Error("Invalid meeting request response after update");
        }

        setData((current: ApiMeetingRequest[]) =>
          current.map((item: ApiMeetingRequest) =>
            item.id === id ? result : item,
          ),
        );

        setSelected((current: ApiMeetingRequest | null) => {
          if (!current) return null;

          return current.id === id ? result : current;
        });

        return result;
      } catch (err: unknown) {
        const message =
          err instanceof Error
            ? err.message
            : "Failed to update meeting request";

        setError(message);

        return null;
      } finally {
        setSaving(false);
      }
    },
    [],
  );

  const sendMeetingRequest = useCallback(async (id: number) => {
    try {
      setSending(true);
      setSendError(null);

      const rawResult: unknown = await meetingRequestService.sendRequest(id);
      const result = extractMeetingRequestFromSendResponse(rawResult);

      if (!result) {
        throw new Error("Invalid meeting request response after sending");
      }

      setData((current: ApiMeetingRequest[]) =>
        current
          .filter((item) => isApiMeetingRequest(item))
          .map((item: ApiMeetingRequest) =>
            item.id === id ? result : item,
          ),
      );

      setSelected((current: ApiMeetingRequest | null) => {
        if (!current) return null;

        return current.id === id ? result : current;
      });

      return result;
    } catch (err: unknown) {
      const message =
        err instanceof Error ? err.message : "Failed to send meeting request";

      setSendError(message);

      return null;
    } finally {
      setSending(false);
    }
  }, []);

  const deleteMeetingRequest = useCallback(async (id: number) => {
    try {
      setDeleting(true);
      setError(null);

      await meetingRequestService.delete(id);

      setData((current: ApiMeetingRequest[]) =>
        current.filter((item: ApiMeetingRequest) => item.id !== id),
      );

      setSelected((current: ApiMeetingRequest | null) => {
        if (!current) return null;

        return current.id === id ? null : current;
      });

      return true;
    } catch (err: unknown) {
      const message =
        err instanceof Error
          ? err.message
          : "Failed to delete meeting request";

      setError(message);

      return false;
    } finally {
      setDeleting(false);
    }
  }, []);

  const clearSelectedMeetingRequest = useCallback(() => {
    setSelected(null);
  }, []);

  const clearMeetingRequestError = useCallback(() => {
    setError(null);
  }, []);

  const clearSendMeetingRequestError = useCallback(() => {
    setSendError(null);
  }, []);

  return {
    data,
    selected,
    agencies,
    workingGroups,
    issues,
    categories,

    loading,
    detailLoading,
    loadingAgencies,
    loadingWorkingGroups,
    loadingIssues,
    loadingCategories,
    saving,
    deleting,
    sending,

    error,
    sendError,
    agencyError,
    workingGroupError,
    issueError,

    fetchGovernmentAgencies,
    fetchWorkingGroups,
    fetchWorkingGroupIssues,
    fetchCategories,
    createWorkingGroupIssue,

    fetchMeetingRequests,
    fetchMeetingRequestById,
    createMeetingRequest,
    updateMeetingRequest,
    sendMeetingRequest,
    deleteMeetingRequest,

    clearSelectedMeetingRequest,
    clearMeetingRequestError,
    clearSendMeetingRequestError,
  };
}