"use client";

import { useCallback, useEffect, useState } from "react";
import { createClient, getBrowserUserId } from "@/lib/supabase/client";
import {
  buildDefaultAchievementMilestones,
  normalizeAchievementStatus,
  resolveAchievementStatus,
} from "@/lib/achievements";
import { useWorkspaceStore } from "@/stores/workspace-store";
import type {
  Achievement,
  AchievementMilestone,
  AchievementMilestoneTaskLink,
  AchievementProgressSource,
  AchievementRewardClaim,
  AchievementStatus,
  Project,
  Task,
} from "@/types";

export interface AchievementMilestoneDraft {
  description?: string | null;
  linked_task_ids?: string[];
  milestone_value: number;
  sort_order: number;
  title: string;
  unlocked_at?: string | null;
}

export interface AchievementUpsertInput {
  background_image_url?: string | null;
  category: string;
  current_progress: number;
  deletable?: boolean;
  description?: string | null;
  editable?: boolean;
  icon: string;
  image_url?: string | null;
  linked_project_ids?: string[];
  linked_task_ids?: string[];
  milestones?: AchievementMilestoneDraft[];
  predefined?: boolean;
  progress_metric_key?: string | null;
  progress_source: AchievementProgressSource;
  reward_description?: string | null;
  reward_image_url?: string | null;
  reward_title: string;
  reward_why?: string | null;
  starter_template_key?: string | null;
  status: AchievementStatus;
  target_value: number;
  title: string;
}

export type AchievementTaskOption = Pick<Task, "id" | "title" | "status">;
export type AchievementProjectOption = Pick<Project, "id" | "name" | "color" | "icon">;

function sortMilestoneDrafts(milestones: AchievementMilestoneDraft[] | undefined) {
  return [...(milestones || [])]
    .filter((milestone) => milestone.title.trim() && milestone.milestone_value >= 0)
    .sort(
      (left, right) =>
        left.milestone_value - right.milestone_value || left.sort_order - right.sort_order,
    )
    .map((milestone, index) => ({
      ...milestone,
      description: milestone.description?.trim() || null,
      linked_task_ids: [...new Set(milestone.linked_task_ids || [])],
      sort_order: index,
      title: milestone.title.trim(),
      unlocked_at: milestone.unlocked_at || null,
    }));
}

function defaultMilestonesForInput(input: AchievementUpsertInput): AchievementMilestoneDraft[] {
  const normalizedMilestones = sortMilestoneDrafts(input.milestones);
  if (normalizedMilestones.length > 0) return normalizedMilestones;
  return buildDefaultAchievementMilestones(input.target_value).map((milestone) => ({
    ...milestone,
    linked_task_ids: [],
    unlocked_at: null,
  }));
}

export function useAchievements() {
  const [achievements, setAchievements] = useState<Achievement[]>([]);
  const [availableTasks, setAvailableTasks] = useState<AchievementTaskOption[]>([]);
  const [availableProjects, setAvailableProjects] = useState<AchievementProjectOption[]>([]);
  const [loading, setLoading] = useState(true);
  const [userId, setUserId] = useState<string | null>(null);
  const supabase = createClient();
  const activeTeamId = useWorkspaceStore((state) => state.activeTeamId);
  const initialized = useWorkspaceStore((state) => state.initialized);

  useEffect(() => {
    void getBrowserUserId().then(setUserId).catch(() => setUserId(null));
  }, []);

  const requireCurrentUserId = useCallback(async () => {
    const currentUserId = userId || (await getBrowserUserId());
    if (currentUserId && currentUserId !== userId) {
      setUserId(currentUserId);
    }
    return currentUserId;
  }, [userId]);

  const ensureStarterAchievements = useCallback(async () => {
    try {
      const currentUserId = await requireCurrentUserId();
      if (!currentUserId) return false;

      const { error } = await supabase.rpc("ensure_starter_achievements", {
        target_team_uuid: activeTeamId,
      });

      if (error) {
        console.warn("Could not seed starter achievements", error);
        return false;
      }

      return true;
    } catch (error) {
      console.warn("Could not seed starter achievements", error);
      return false;
    }
  }, [activeTeamId, requireCurrentUserId, supabase]);

  const fetchAchievements = useCallback(async () => {
    if (!initialized) return;

    setLoading(true);
    const currentUserId = await requireCurrentUserId();

    if (!currentUserId) {
      setAchievements([]);
      setAvailableTasks([]);
      setAvailableProjects([]);
      setLoading(false);
      return;
    }

    const loadAchievementRows = async () => {
      let achievementsQuery = supabase
        .from("achievements")
        .select("*")
        .order("completed_at", { ascending: false, nullsFirst: false })
        .order("sort_order")
        .order("created_at", { ascending: false });

      achievementsQuery = activeTeamId
        ? achievementsQuery.eq("team_id", activeTeamId)
        : achievementsQuery.is("team_id", null);

      const { data, error } = await achievementsQuery;

      if (error) {
        console.warn("Could not load achievements", error);
        return [] as Achievement[];
      }

      return (data || []) as Achievement[];
    };

    const tasksQuery = activeTeamId
      ? supabase
          .from("tasks")
          .select("id, title, status")
          .eq("team_id", activeTeamId)
          .is("parent_task_id", null)
          .order("created_at", { ascending: false })
          .limit(100)
      : supabase
          .from("tasks")
          .select("id, title, status")
          .is("team_id", null)
          .is("parent_task_id", null)
          .order("created_at", { ascending: false })
          .limit(100);

    const projectsQuery = activeTeamId
      ? supabase
          .from("projects")
          .select("id, name, color, icon")
          .eq("team_id", activeTeamId)
          .order("created_at", { ascending: false })
          .limit(100)
      : supabase
          .from("projects")
          .select("id, name, color, icon")
          .is("team_id", null)
          .order("created_at", { ascending: false })
          .limit(100);

    const [initialAchievementRows, { data: tasksData }, { data: projectsData }] =
      await Promise.all([loadAchievementRows(), tasksQuery, projectsQuery]);

    let achievementRows = initialAchievementRows;
    const hasStarterTemplates = achievementRows.some(
      (achievement) => achievement.predefined && achievement.starter_template_key,
    );

    if (!hasStarterTemplates) {
      const seeded = await ensureStarterAchievements();
      if (seeded) {
        achievementRows = await loadAchievementRows();
      }
    }

    const achievementIds = achievementRows.map((achievement) => achievement.id);
    const needsCompletedTaskCount = achievementRows.some(
      (achievement) => achievement.progress_source === "completed_tasks_count",
    );

    const milestonesQuery =
      achievementIds.length > 0
        ? supabase
            .from("achievement_milestones")
            .select("*")
            .in("achievement_id", achievementIds)
            .order("milestone_value")
            .order("sort_order")
        : Promise.resolve({ data: [] as AchievementMilestone[] });

    const taskLinksQuery =
      achievementIds.length > 0
        ? supabase
            .from("achievement_task_links")
            .select("achievement_id, task_id, created_at, task:tasks(id, title, status)")
            .in("achievement_id", achievementIds)
        : Promise.resolve({ data: [] });

    const projectLinksQuery =
      achievementIds.length > 0
        ? supabase
            .from("achievement_project_links")
            .select("achievement_id, project_id, created_at, project:projects(id, name, color, icon)")
            .in("achievement_id", achievementIds)
        : Promise.resolve({ data: [] });

    const rewardClaimsQuery =
      achievementIds.length > 0
        ? supabase
            .from("achievement_reward_claims")
            .select("*")
            .eq("user_id", currentUserId)
            .in("achievement_id", achievementIds)
        : Promise.resolve({ data: [] as AchievementRewardClaim[] });

    const completedTasksQuery = needsCompletedTaskCount
      ? activeTeamId
        ? supabase
            .from("tasks")
            .select("id", { count: "exact", head: true })
            .eq("team_id", activeTeamId)
            .eq("status", "done")
        : supabase
            .from("tasks")
            .select("id", { count: "exact", head: true })
            .is("team_id", null)
            .eq("status", "done")
      : Promise.resolve({ count: 0 });

    const [
      { data: milestonesData },
      { data: taskLinksData },
      { data: projectLinksData },
      { data: rewardClaimsData },
      completedTasksResult,
    ] = await Promise.all([
      milestonesQuery,
      taskLinksQuery,
      projectLinksQuery,
      rewardClaimsQuery,
      completedTasksQuery,
    ]);

    const milestoneIds = ((milestonesData || []) as AchievementMilestone[]).map(
      (milestone) => milestone.id,
    );
    const { data: milestoneTaskLinksData } =
      milestoneIds.length > 0
        ? await supabase
            .from("achievement_milestone_task_links")
            .select("milestone_id, task_id, created_at, task:tasks(id, title, status)")
            .in("milestone_id", milestoneIds)
        : { data: [] };

    const completedTasksCount = completedTasksResult.count || 0;

    const normalizedMilestoneTaskLinks = ((milestoneTaskLinksData || []) as Array<{
      created_at: string;
      milestone_id: string;
      task: AchievementMilestoneTaskLink["task"][] | AchievementMilestoneTaskLink["task"] | null;
      task_id: string;
    }>).map((taskLink) => ({
      created_at: taskLink.created_at,
      milestone_id: taskLink.milestone_id,
      task: Array.isArray(taskLink.task) ? taskLink.task[0] || null : taskLink.task || null,
      task_id: taskLink.task_id,
    }));

    const milestoneTaskLinksByMilestone = new Map<string, AchievementMilestoneTaskLink[]>();
    for (const taskLink of normalizedMilestoneTaskLinks) {
      const next = milestoneTaskLinksByMilestone.get(taskLink.milestone_id) || [];
      next.push(taskLink);
      milestoneTaskLinksByMilestone.set(taskLink.milestone_id, next);
    }

    const milestonesByAchievement = new Map<string, AchievementMilestone[]>();
    for (const milestone of (milestonesData || []) as AchievementMilestone[]) {
      const next = milestonesByAchievement.get(milestone.achievement_id) || [];
      next.push({
        ...milestone,
        task_links: milestoneTaskLinksByMilestone.get(milestone.id) || [],
      });
      milestonesByAchievement.set(milestone.achievement_id, next);
    }

    const normalizedTaskLinks = ((taskLinksData || []) as Array<{
      achievement_id: string;
      created_at: string;
      task: Achievement["task_links"] extends Array<infer Link>
        ? Link extends { task?: infer TaskValue }
          ? TaskValue[] | TaskValue | null
          : never
        : never;
      task_id: string;
    }>).map((taskLink) => ({
      achievement_id: taskLink.achievement_id,
      created_at: taskLink.created_at,
      task: Array.isArray(taskLink.task) ? taskLink.task[0] || null : taskLink.task || null,
      task_id: taskLink.task_id,
    }));

    const taskLinksByAchievement = new Map<string, NonNullable<Achievement["task_links"]>>();
    for (const taskLink of normalizedTaskLinks) {
      const next = taskLinksByAchievement.get(taskLink.achievement_id) || [];
      next.push(taskLink);
      taskLinksByAchievement.set(taskLink.achievement_id, next);
    }

    const normalizedProjectLinks = ((projectLinksData || []) as Array<{
      achievement_id: string;
      created_at: string;
      project: Achievement["project_links"] extends Array<infer Link>
        ? Link extends { project?: infer ProjectValue }
          ? ProjectValue[] | ProjectValue | null
          : never
        : never;
      project_id: string;
    }>).map((projectLink) => ({
      achievement_id: projectLink.achievement_id,
      created_at: projectLink.created_at,
      project: Array.isArray(projectLink.project)
        ? projectLink.project[0] || null
        : projectLink.project || null,
      project_id: projectLink.project_id,
    }));

    const projectLinksByAchievement = new Map<string, NonNullable<Achievement["project_links"]>>();
    for (const projectLink of normalizedProjectLinks) {
      const next = projectLinksByAchievement.get(projectLink.achievement_id) || [];
      next.push(projectLink);
      projectLinksByAchievement.set(projectLink.achievement_id, next);
    }

    const rewardClaimByAchievement = new Map<string, AchievementRewardClaim>();
    for (const claim of (rewardClaimsData || []) as AchievementRewardClaim[]) {
      rewardClaimByAchievement.set(claim.achievement_id, claim);
    }

    const hydratedAchievements = achievementRows.map((achievement) => {
      const taskLinks = taskLinksByAchievement.get(achievement.id) || [];
      const projectLinks = projectLinksByAchievement.get(achievement.id) || [];
      const milestones = milestonesByAchievement.get(achievement.id) || [];

      let resolvedProgress = achievement.current_progress;
      if (achievement.progress_source === "completed_tasks_count") {
        resolvedProgress = completedTasksCount;
      } else if (achievement.progress_source === "linked_tasks_completed_count") {
        resolvedProgress = taskLinks.filter((taskLink) => taskLink.task?.status === "done").length;
      }

      const resolvedMilestones = milestones.map((milestone) => {
        const milestoneTaskLinks = milestone.task_links || [];
        const completedLinkedTaskCount = milestoneTaskLinks.filter(
          (taskLink) => taskLink.task?.status === "done",
        ).length;
        const allLinkedTasksDone =
          milestoneTaskLinks.length > 0 && completedLinkedTaskCount === milestoneTaskLinks.length;
        const manualCompleted = Boolean(milestone.unlocked_at);
        const progressCompleted = resolvedProgress >= milestone.milestone_value;
        const resolvedCompleted = manualCompleted || allLinkedTasksDone || progressCompleted;

        return {
          ...milestone,
          completed_linked_task_count: completedLinkedTaskCount,
          linked_task_count: milestoneTaskLinks.length,
          resolved_completed: resolvedCompleted,
          resolved_completion_source: manualCompleted
            ? "manual"
            : allLinkedTasksDone
              ? "linked_tasks"
              : progressCompleted
                ? "progress"
                : null,
        } satisfies AchievementMilestone;
      });
      const allMilestonesCompleted =
        resolvedMilestones.length > 0 &&
        resolvedMilestones.every((milestone) => milestone.resolved_completed);
      const effectiveProgress = allMilestonesCompleted
        ? Math.max(resolvedProgress, achievement.target_value)
        : resolvedProgress;

      const resolvedStatus = normalizeAchievementStatus(
        effectiveProgress,
        achievement.target_value,
        achievement.status,
      );

      return {
        ...achievement,
        milestones: resolvedMilestones,
        project_links: projectLinks,
        reward_claim: rewardClaimByAchievement.get(achievement.id) || null,
        resolved_progress: effectiveProgress,
        resolved_status: resolvedStatus,
        task_links: taskLinks,
      } satisfies Achievement;
    });

    setAchievements(hydratedAchievements);
    setAvailableTasks((tasksData || []) as AchievementTaskOption[]);
    setAvailableProjects((projectsData || []) as AchievementProjectOption[]);
    setLoading(false);
  }, [activeTeamId, ensureStarterAchievements, initialized, requireCurrentUserId, supabase]);

  useEffect(() => {
    if (!initialized) return;
    const timer = window.setTimeout(() => {
      void fetchAchievements();
    }, 0);

    return () => window.clearTimeout(timer);
  }, [fetchAchievements, initialized]);

  const replaceAchievementRelations = useCallback(
    async (achievementId: string, input: AchievementUpsertInput) => {
      await Promise.all([
        supabase.from("achievement_milestones").delete().eq("achievement_id", achievementId),
        supabase.from("achievement_task_links").delete().eq("achievement_id", achievementId),
        supabase.from("achievement_project_links").delete().eq("achievement_id", achievementId),
      ]);

      const milestones = defaultMilestonesForInput(input);
      if (milestones.length > 0) {
        const { data, error } = await supabase
          .from("achievement_milestones")
          .insert(
            milestones.map((milestone, index) => ({
              achievement_id: achievementId,
              description: milestone.description || null,
              milestone_value: milestone.milestone_value,
              sort_order: index,
              title: milestone.title,
              unlocked_at: milestone.unlocked_at || null,
            })),
          )
          .select("id, sort_order");

        if (error) throw error;

        const milestoneIdsBySortOrder = new Map(
          (data || []).map((milestone) => [milestone.sort_order, milestone.id]),
        );
        const milestoneTaskLinks = milestones.flatMap((milestone, index) => {
          const milestoneId = milestoneIdsBySortOrder.get(index);
          if (!milestoneId) return [];

          return [...new Set(milestone.linked_task_ids || [])].map((taskId) => ({
            achievement_id: achievementId,
            milestone_id: milestoneId,
            task_id: taskId,
          }));
        });

        if (milestoneTaskLinks.length > 0) {
          const { error: milestoneTaskLinkError } = await supabase
            .from("achievement_milestone_task_links")
            .insert(
              milestoneTaskLinks.map(({ milestone_id, task_id }) => ({
                milestone_id,
                task_id,
              })),
            );
          if (milestoneTaskLinkError) throw milestoneTaskLinkError;
        }
      }

      const linkedTaskIds = [...new Set(input.linked_task_ids || [])];
      if (linkedTaskIds.length > 0) {
        const { error } = await supabase.from("achievement_task_links").insert(
          linkedTaskIds.map((taskId) => ({
            achievement_id: achievementId,
            task_id: taskId,
          })),
        );
        if (error) throw error;
      }

      const linkedProjectIds = [...new Set(input.linked_project_ids || [])];
      if (linkedProjectIds.length > 0) {
        const { error } = await supabase.from("achievement_project_links").insert(
          linkedProjectIds.map((projectId) => ({
            achievement_id: achievementId,
            project_id: projectId,
          })),
        );
        if (error) throw error;
      }
    },
    [supabase],
  );

  const createAchievement = useCallback(
    async (input: AchievementUpsertInput) => {
      const currentUserId = await requireCurrentUserId();
      if (!currentUserId) throw new Error("Not authenticated");
      const persistedProgress =
        input.status === "completed"
          ? Math.max(input.current_progress, input.target_value)
          : input.current_progress;

      const { data, error } = await supabase
        .from("achievements")
        .insert({
          background_image_url: input.background_image_url || null,
          category: input.category.trim() || "productivity",
          completed_at: input.status === "completed" ? new Date().toISOString() : null,
          current_progress: persistedProgress,
          deletable: input.deletable ?? true,
          description: input.description?.trim() || null,
          editable: input.editable ?? true,
          icon: input.icon.trim() || "🏆",
          image_url: input.image_url || null,
          predefined: input.predefined ?? false,
          progress_metric_key: input.progress_metric_key || null,
          progress_source: input.progress_source,
          reward_description: input.reward_description?.trim() || null,
          reward_image_url: input.reward_image_url || null,
          reward_title: input.reward_title.trim(),
          reward_why: input.reward_why?.trim() || null,
          sort_order: achievements.length,
          starter_template_key: input.starter_template_key || null,
          status: input.status,
          target_value: input.target_value,
          team_id: activeTeamId,
          title: input.title.trim(),
          user_id: currentUserId,
        })
        .select()
        .single();

      if (error) throw error;

      await replaceAchievementRelations(data.id, input);
      await fetchAchievements();
      return data as Achievement;
    },
    [
      achievements.length,
      activeTeamId,
      fetchAchievements,
      replaceAchievementRelations,
      requireCurrentUserId,
      supabase,
    ],
  );

  const updateAchievement = useCallback(
    async (achievementId: string, input: AchievementUpsertInput) => {
      const persistedProgress =
        input.status === "completed"
          ? Math.max(input.current_progress, input.target_value)
          : input.current_progress;

      const { error } = await supabase
        .from("achievements")
        .update({
          background_image_url: input.background_image_url || null,
          category: input.category.trim() || "productivity",
          completed_at: input.status === "completed" ? new Date().toISOString() : null,
          current_progress: persistedProgress,
          deletable: input.deletable ?? true,
          description: input.description?.trim() || null,
          editable: input.editable ?? true,
          icon: input.icon.trim() || "🏆",
          image_url: input.image_url || null,
          predefined: input.predefined ?? false,
          progress_metric_key: input.progress_metric_key || null,
          progress_source: input.progress_source,
          reward_description: input.reward_description?.trim() || null,
          reward_image_url: input.reward_image_url || null,
          reward_title: input.reward_title.trim(),
          reward_why: input.reward_why?.trim() || null,
          starter_template_key: input.starter_template_key || null,
          status: input.status,
          target_value: input.target_value,
          title: input.title.trim(),
        })
        .eq("id", achievementId);

      if (error) throw error;

      await replaceAchievementRelations(achievementId, input);
      await fetchAchievements();
    },
    [fetchAchievements, replaceAchievementRelations, supabase],
  );

  const toggleMilestoneCompletion = useCallback(
    async (milestoneId: string, completed: boolean) => {
      const { error } = await supabase
        .from("achievement_milestones")
        .update({ unlocked_at: completed ? new Date().toISOString() : null })
        .eq("id", milestoneId);

      if (error) throw error;

      await fetchAchievements();
    },
    [fetchAchievements, supabase],
  );

  const deleteAchievement = useCallback(
    async (achievementId: string) => {
      const achievement = achievements.find((item) => item.id === achievementId);
      if (achievement && !achievement.deletable) return;

      const { error } = await supabase.from("achievements").delete().eq("id", achievementId);
      if (error) throw error;

      setAchievements((current) => current.filter((achievement) => achievement.id !== achievementId));
    },
    [achievements, supabase],
  );

  const duplicateAchievement = useCallback(
    async (achievementId: string) => {
      const achievement = achievements.find((item) => item.id === achievementId);
      if (!achievement) return null;

      const duplicated = await createAchievement({
        background_image_url: achievement.background_image_url,
        category: achievement.category,
        current_progress: 0,
        deletable: true,
        description: achievement.description,
        editable: true,
        icon: achievement.icon,
        image_url: achievement.image_url,
        linked_project_ids: (achievement.project_links || []).map((link) => link.project_id),
        linked_task_ids: (achievement.task_links || []).map((link) => link.task_id),
        milestones: (achievement.milestones || []).map((milestone, index) => ({
          description: milestone.description,
          linked_task_ids: (milestone.task_links || []).map((link) => link.task_id),
          milestone_value: milestone.milestone_value,
          sort_order: index,
          title: milestone.title,
          unlocked_at: null,
        })),
        predefined: false,
        progress_metric_key: achievement.progress_metric_key,
        progress_source: achievement.progress_source,
        reward_description: achievement.reward_description,
        reward_image_url: achievement.reward_image_url,
        reward_title: achievement.reward_title,
        reward_why: achievement.reward_why,
        starter_template_key: null,
        status: "not_started",
        target_value: achievement.target_value,
        title: `${achievement.title} Copy`,
      });

      return duplicated;
    },
    [achievements, createAchievement],
  );

  const claimAchievementReward = useCallback(
    async (achievementId: string) => {
      const currentUserId = await requireCurrentUserId();
      if (!currentUserId) throw new Error("Not authenticated");

      const achievement = achievements.find((item) => item.id === achievementId);
      if (!achievement) throw new Error("Achievement nicht gefunden.");
      if (!achievement.reward_title) throw new Error("Für dieses Achievement ist kein Reward gesetzt.");
      if (resolveAchievementStatus(achievement) !== "completed") {
        throw new Error("Schließe das Achievement zuerst ab, bevor du den Reward claimst.");
      }

      const { error } = await supabase
        .from("achievement_reward_claims")
        .upsert(
          {
            achievement_id: achievementId,
            claimed_at: new Date().toISOString(),
            team_id: achievement?.team_id ?? activeTeamId,
            user_id: currentUserId,
          },
          { onConflict: "achievement_id,user_id" },
        );

      if (error) throw error;

      await fetchAchievements();
    },
    [achievements, activeTeamId, fetchAchievements, requireCurrentUserId, supabase],
  );

  return {
    achievements,
    availableProjects,
    availableTasks,
    claimAchievementReward,
    createAchievement,
    deleteAchievement,
    duplicateAchievement,
    loading,
    refetch: fetchAchievements,
    toggleMilestoneCompletion,
    updateAchievement,
  };
}
