import { useState, useEffect, useCallback, useRef } from 'react';
import projectService, {
  type Project,
  type Task,
  type Timesheet,
  type ProjectTemplate,
  type ActivityType,
  type ProjectListParams,
} from '../services/projectService';

/** Normalized key so pagination fields always affect deps (JSON.stringify drops `undefined`). */
function listQueryKey(p: ProjectListParams): string {
  return JSON.stringify({
    filters: p.filters ?? {},
    appendFilters: p.appendFilters ?? [],
    fields: p.fields ?? null,
    limit_start: p.limit_start ?? 0,
    limit_page_length: p.limit_page_length ?? 20,
    order_by: p.order_by ?? '',
  });
}

// ─── Project list ────────────────────────────────────────────────────────────
export const useProjectList = (params: ProjectListParams = {}) => {
  const [projects, setProjects] = useState<Project[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [totalCount, setTotalCount] = useState(0);

  const paramsKey = listQueryKey(params);
  const fetchSeqRef = useRef(0);

  useEffect(() => {
    const reqId = ++fetchSeqRef.current;
    let cancelled = false;
    (async () => {
      try {
        setLoading(true);
        setError(null);
        const [response, count] = await Promise.all([
          projectService.getProjects(params),
          projectService.getProjectCount(params.filters),
        ]);
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setProjects(response.data);
        setTotalCount(count);
      } catch (err) {
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setError(err instanceof Error ? err.message : 'Failed to fetch projects');
      } finally {
        if (!cancelled && reqId === fetchSeqRef.current) setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [paramsKey]);

  const refetch = useCallback(async () => {
    const reqId = ++fetchSeqRef.current;
    try {
      setLoading(true);
      setError(null);
      const [response, count] = await Promise.all([
        projectService.getProjects(params),
        projectService.getProjectCount(params.filters),
      ]);
      if (reqId !== fetchSeqRef.current) return;
      setProjects(response.data);
      setTotalCount(count);
    } catch (err) {
      if (reqId !== fetchSeqRef.current) return;
      setError(err instanceof Error ? err.message : 'Failed to fetch projects');
    } finally {
      if (reqId === fetchSeqRef.current) setLoading(false);
    }
  }, [paramsKey]);

  return { projects, loading, error, totalCount, refetch };
};

// ─── Project detail ──────────────────────────────────────────────────────────
export const useProjectDetails = (projectName: string | null) => {
  const [project, setProject] = useState<Project | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchProject = useCallback(async () => {
    if (!projectName) { setProject(null); return; }
    try {
      setLoading(true);
      setError(null);
      setProject(await projectService.getProject(projectName));
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to fetch project');
    } finally {
      setLoading(false);
    }
  }, [projectName]);

  useEffect(() => { fetchProject(); }, [fetchProject]);
  return { project, loading, error, refetch: fetchProject };
};

// ─── Task list (generic, filterable by project) ──────────────────────────────
export const useTaskList = (params: ProjectListParams = {}) => {
  const [tasks, setTasks] = useState<Task[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [totalCount, setTotalCount] = useState(0);

  const paramsKey = listQueryKey(params);
  const fetchSeqRef = useRef(0);

  useEffect(() => {
    const reqId = ++fetchSeqRef.current;
    let cancelled = false;
    (async () => {
      try {
        setLoading(true);
        setError(null);
        const [response, count] = await Promise.all([
          projectService.getTasks(params),
          projectService.getTaskCount(params.filters),
        ]);
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setTasks(response.data);
        setTotalCount(count);
      } catch (err) {
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setError(err instanceof Error ? err.message : 'Failed to fetch tasks');
      } finally {
        if (!cancelled && reqId === fetchSeqRef.current) setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [paramsKey]);

  const refetch = useCallback(async () => {
    const reqId = ++fetchSeqRef.current;
    try {
      setLoading(true);
      setError(null);
      const [response, count] = await Promise.all([
        projectService.getTasks(params),
        projectService.getTaskCount(params.filters),
      ]);
      if (reqId !== fetchSeqRef.current) return;
      setTasks(response.data);
      setTotalCount(count);
    } catch (err) {
      if (reqId !== fetchSeqRef.current) return;
      setError(err instanceof Error ? err.message : 'Failed to fetch tasks');
    } finally {
      if (reqId === fetchSeqRef.current) setLoading(false);
    }
  }, [paramsKey]);

  return { tasks, loading, error, totalCount, refetch };
};

// ─── Tasks for a project ─────────────────────────────────────────────────────
export const useProjectTasks = (projectName: string | null) => {
  const [tasks, setTasks] = useState<Task[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchTasks = useCallback(async () => {
    if (!projectName) { setTasks([]); return; }
    try {
      setLoading(true);
      setError(null);
      const { data } = await projectService.getTasksForProject(projectName);
      setTasks(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to fetch tasks');
    } finally {
      setLoading(false);
    }
  }, [projectName]);

  useEffect(() => { fetchTasks(); }, [fetchTasks]);
  return { tasks, loading, error, refetch: fetchTasks };
};

// ─── Task detail ─────────────────────────────────────────────────────────────
export const useTaskDetails = (taskName: string | null) => {
  const [task, setTask] = useState<Task | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchTask = useCallback(async () => {
    if (!taskName) { setTask(null); return; }
    try {
      setLoading(true);
      setError(null);
      setTask(await projectService.getTask(taskName));
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to fetch task');
    } finally {
      setLoading(false);
    }
  }, [taskName]);

  useEffect(() => { fetchTask(); }, [fetchTask]);
  return { task, loading, error, refetch: fetchTask };
};

// ─── Timesheet list (generic) ────────────────────────────────────────────────
export const useTimesheetList = (params: ProjectListParams = {}) => {
  const [timesheets, setTimesheets] = useState<Timesheet[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [totalCount, setTotalCount] = useState(0);

  const paramsKey = listQueryKey(params);
  const fetchSeqRef = useRef(0);

  useEffect(() => {
    const reqId = ++fetchSeqRef.current;
    let cancelled = false;
    (async () => {
      try {
        setLoading(true);
        setError(null);
        const [response, count] = await Promise.all([
          projectService.getTimesheets(params),
          projectService.getTimesheetCount(params.filters || {}, params.appendFilters || []),
        ]);
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setTimesheets(response.data);
        setTotalCount(count);
      } catch (err) {
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setError(err instanceof Error ? err.message : 'Failed to fetch timesheets');
      } finally {
        if (!cancelled && reqId === fetchSeqRef.current) setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [paramsKey]);

  const refetch = useCallback(async () => {
    const reqId = ++fetchSeqRef.current;
    try {
      setLoading(true);
      setError(null);
      const [response, count] = await Promise.all([
        projectService.getTimesheets(params),
        projectService.getTimesheetCount(params.filters || {}, params.appendFilters || []),
      ]);
      if (reqId !== fetchSeqRef.current) return;
      setTimesheets(response.data);
      setTotalCount(count);
    } catch (err) {
      if (reqId !== fetchSeqRef.current) return;
      setError(err instanceof Error ? err.message : 'Failed to fetch timesheets');
    } finally {
      if (reqId === fetchSeqRef.current) setLoading(false);
    }
  }, [paramsKey]);

  return { timesheets, loading, error, totalCount, refetch };
};

// ─── Timesheets for a project ────────────────────────────────────────────────
export const useProjectTimesheets = (projectName: string | null) => {
  const [timesheets, setTimesheets] = useState<Timesheet[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchTimesheets = useCallback(async () => {
    if (!projectName) { setTimesheets([]); return; }
    try {
      setLoading(true);
      setError(null);
      const { data } = await projectService.getTimesheetsForProject(projectName);
      setTimesheets(data);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to fetch timesheets');
    } finally {
      setLoading(false);
    }
  }, [projectName]);

  useEffect(() => { fetchTimesheets(); }, [fetchTimesheets]);
  return { timesheets, loading, error, refetch: fetchTimesheets };
};

// ─── Timesheet detail ────────────────────────────────────────────────────────
export const useTimesheetDetails = (name: string | null) => {
  const [timesheet, setTimesheet] = useState<Timesheet | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchTimesheet = useCallback(async () => {
    if (!name) { setTimesheet(null); return; }
    try {
      setLoading(true);
      setError(null);
      setTimesheet(await projectService.getTimesheet(name));
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to fetch timesheet');
    } finally {
      setLoading(false);
    }
  }, [name]);

  useEffect(() => { fetchTimesheet(); }, [fetchTimesheet]);
  return { timesheet, loading, error, refetch: fetchTimesheet };
};

// ─── Project Templates ────────────────────────────────────────────────────────
export const useProjectTemplates = (params: ProjectListParams = {}) => {
  const [templates, setTemplates] = useState<ProjectTemplate[]>([]);
  const [loading, setLoading] = useState(true);
  const [totalCount, setTotalCount] = useState(0);

  const paramsKey = listQueryKey(params);
  const fetchSeqRef = useRef(0);

  useEffect(() => {
    const reqId = ++fetchSeqRef.current;
    let cancelled = false;
    (async () => {
      try {
        setLoading(true);
        const [r, count] = await Promise.all([
          projectService.getProjectTemplates(params),
          projectService.getProjectTemplateCount(params.filters || {}),
        ]);
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setTemplates(r.data);
        setTotalCount(count);
      } catch { /* silent */ } finally { if (!cancelled && reqId === fetchSeqRef.current) setLoading(false); }
    })();
    return () => { cancelled = true; };
  }, [paramsKey]);

  const refetch = useCallback(async () => {
    const reqId = ++fetchSeqRef.current;
    try {
      setLoading(true);
      const [r, count] = await Promise.all([
        projectService.getProjectTemplates(params),
        projectService.getProjectTemplateCount(params.filters || {}),
      ]);
      if (reqId !== fetchSeqRef.current) return;
      setTemplates(r.data);
      setTotalCount(count);
    } catch { /* silent */ } finally { if (reqId === fetchSeqRef.current) setLoading(false); }
  }, [paramsKey]);

  return { templates, loading, totalCount, refetch };
};

export const useProjectTemplateDetails = (name: string | null) => {
  const [template, setTemplate] = useState<ProjectTemplate | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchTemplate = useCallback(async () => {
    if (!name) { setTemplate(null); return; }
    try {
      setLoading(true); setError(null);
      setTemplate(await projectService.getProjectTemplate(name));
    } catch (err) { setError(err instanceof Error ? err.message : 'Failed to fetch template'); }
    finally { setLoading(false); }
  }, [name]);

  useEffect(() => { fetchTemplate(); }, [fetchTemplate]);
  return { template, loading, error, refetch: fetchTemplate };
};

// ─── Activity Types ───────────────────────────────────────────────────────────
export const useActivityTypeList = (params: ProjectListParams = {}) => {
  const [activityTypes, setActivityTypes] = useState<ActivityType[]>([]);
  const [loading, setLoading] = useState(true);
  const [totalCount, setTotalCount] = useState(0);

  const paramsKey = listQueryKey(params);
  const fetchSeqRef = useRef(0);

  useEffect(() => {
    const reqId = ++fetchSeqRef.current;
    let cancelled = false;
    (async () => {
      try {
        setLoading(true);
        const [r, count] = await Promise.all([
          projectService.getActivityTypes(params),
          projectService.getActivityTypeCount(params.filters || {}),
        ]);
        if (cancelled || reqId !== fetchSeqRef.current) return;
        setActivityTypes(r.data);
        setTotalCount(count);
      } catch { /* silent */ } finally { if (!cancelled && reqId === fetchSeqRef.current) setLoading(false); }
    })();
    return () => { cancelled = true; };
  }, [paramsKey]);

  const refetch = useCallback(async () => {
    const reqId = ++fetchSeqRef.current;
    try {
      setLoading(true);
      const [r, count] = await Promise.all([
        projectService.getActivityTypes(params),
        projectService.getActivityTypeCount(params.filters || {}),
      ]);
      if (reqId !== fetchSeqRef.current) return;
      setActivityTypes(r.data);
      setTotalCount(count);
    } catch { /* silent */ } finally { if (reqId === fetchSeqRef.current) setLoading(false); }
  }, [paramsKey]);

  return { activityTypes, loading, totalCount, refetch };
};

export const useActivityTypeDetails = (name: string | null) => {
  const [activityType, setActivityType] = useState<ActivityType | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const fetchActivityType = useCallback(async () => {
    if (!name) { setActivityType(null); return; }
    try {
      setLoading(true); setError(null);
      setActivityType(await projectService.getActivityType(name));
    } catch (err) { setError(err instanceof Error ? err.message : 'Failed to fetch activity type'); }
    finally { setLoading(false); }
  }, [name]);

  useEffect(() => { fetchActivityType(); }, [fetchActivityType]);
  return { activityType, loading, error, refetch: fetchActivityType };
};

// ─── Mutations ───────────────────────────────────────────────────────────────
export const useProjectMutations = () => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const run = async <T>(fn: () => Promise<T>): Promise<T> => {
    try {
      setLoading(true);
      setError(null);
      return await fn();
    } catch (err) {
      const msg = err instanceof Error ? err.message : 'Operation failed';
      setError(msg);
      throw err;
    } finally {
      setLoading(false);
    }
  };

  return {
    createProject: (data: Partial<Project>) => run(() => projectService.createProject(data)),
    updateProject: (name: string, data: Partial<Project>) => run(() => projectService.updateProject(name, data)),
    createTask: (data: Partial<Task>) => run(() => projectService.createTask(data)),
    updateTask: (name: string, data: Partial<Task>) => run(() => projectService.updateTask(name, data)),
    createTimesheet: (data: Partial<Timesheet>) => run(() => projectService.createTimesheet(data)),
    updateTimesheet: (name: string, data: Partial<Timesheet>) => run(() => projectService.updateTimesheet(name, data)),
    submitTimesheet: (name: string) => run(() => projectService.submitTimesheet(name)),
    cancelTimesheet: (name: string) => run(() => projectService.cancelTimesheet(name)),
    createProjectTemplate: (data: Partial<ProjectTemplate>) => run(() => projectService.createProjectTemplate(data)),
    updateProjectTemplate: (name: string, data: Partial<ProjectTemplate>) => run(() => projectService.updateProjectTemplate(name, data)),
    createActivityType: (data: Partial<ActivityType>) => run(() => projectService.createActivityType(data)),
    updateActivityType: (name: string, data: Partial<ActivityType>) => run(() => projectService.updateActivityType(name, data)),
    loading,
    error,
  };
};
