"use client";

import { useCallback, useEffect, useState } from "react";
import { createClient, getBrowserUserId } from "@/lib/supabase/client";
import { useWorkspaceStore } from "@/stores/workspace-store";
import type { NoteRelation, RelatedKnowledgeNote, Task, Note, Project } from "@/types";

type RelationRow = {
  id: string;
  user_id: string;
  team_id: string | null;
  source_note_id: string;
  target_note_id: string | null;
  target_task_id: string | null;
  target_project_id: string | null;
  created_at: string;
};

function toTargetType(row: RelationRow): NoteRelation["target_type"] {
  if (row.target_note_id) return "note";
  if (row.target_task_id) return "task";
  return "project";
}

export function useNoteKnowledge(input: {
  noteId: string;
  teamId: string | null;
  projectId: string | null;
  tagIds: string[];
}) {
  const { noteId, teamId, projectId, tagIds } = input;
  const supabase = createClient();
  const initialized = useWorkspaceStore((state) => state.initialized);
  const [outbound, setOutbound] = useState<NoteRelation[]>([]);
  const [backlinks, setBacklinks] = useState<NoteRelation[]>([]);
  const [relatedNotes, setRelatedNotes] = useState<RelatedKnowledgeNote[]>([]);
  const [loading, setLoading] = useState(true);
  const [userId, setUserId] = useState<string | null>(null);

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

  async function requireCurrentUserId() {
    const currentUserId = userId || (await getBrowserUserId());
    if (currentUserId && currentUserId !== userId) {
      setUserId(currentUserId);
    }
    return currentUserId;
  }

  const fetchKnowledge = useCallback(async () => {
    if (!initialized || !noteId) return;

    setLoading(true);

    let outboundQuery = supabase.from("note_relations").select("*").eq("source_note_id", noteId);
    let backlinksQuery = supabase.from("note_relations").select("*").eq("target_note_id", noteId);

    outboundQuery = teamId ? outboundQuery.eq("team_id", teamId) : outboundQuery.is("team_id", null);
    backlinksQuery = teamId ? backlinksQuery.eq("team_id", teamId) : backlinksQuery.is("team_id", null);

    const [{ data: outboundRows }, { data: backlinkRows }] = await Promise.all([
      outboundQuery,
      backlinksQuery,
    ]);

    const typedOutbound = (outboundRows || []) as RelationRow[];
    const typedBacklinks = (backlinkRows || []) as RelationRow[];

    const targetNoteIds = typedOutbound
      .map((row) => row.target_note_id)
      .filter((value): value is string => Boolean(value));
    const targetTaskIds = typedOutbound
      .map((row) => row.target_task_id)
      .filter((value): value is string => Boolean(value));
    const targetProjectIds = typedOutbound
      .map((row) => row.target_project_id)
      .filter((value): value is string => Boolean(value));
    const sourceNoteIds = typedBacklinks
      .map((row) => row.source_note_id)
      .filter((value): value is string => Boolean(value));

    const [targetNotesRes, targetTasksRes, targetProjectsRes, sourceNotesRes] = await Promise.all([
      targetNoteIds.length > 0
        ? supabase
            .from("notes")
            .select("id, title, project_id, updated_at")
            .in("id", targetNoteIds)
        : Promise.resolve({ data: [] as Array<Pick<Note, "id" | "title" | "project_id" | "updated_at">> }),
      targetTaskIds.length > 0
        ? supabase
            .from("tasks")
            .select("id, title, status, priority, project_id")
            .in("id", targetTaskIds)
        : Promise.resolve({ data: [] as Array<Pick<Task, "id" | "title" | "status" | "priority" | "project_id">> }),
      targetProjectIds.length > 0
        ? supabase
            .from("projects")
            .select("id, name, icon, color")
            .in("id", targetProjectIds)
        : Promise.resolve({ data: [] as Array<Pick<Project, "id" | "name" | "icon" | "color">> }),
      sourceNoteIds.length > 0
        ? supabase
            .from("notes")
            .select("id, title, project_id, updated_at")
            .in("id", sourceNoteIds)
        : Promise.resolve({ data: [] as Array<Pick<Note, "id" | "title" | "project_id" | "updated_at">> }),
    ]);

    const targetNoteMap = new Map(
      ((targetNotesRes.data || []) as Array<Pick<Note, "id" | "title" | "project_id" | "updated_at">>).map((note) => [
        note.id,
        note,
      ])
    );
    const targetTaskMap = new Map(
      ((targetTasksRes.data || []) as Array<Pick<Task, "id" | "title" | "status" | "priority" | "project_id">>).map(
        (task) => [task.id, task]
      )
    );
    const targetProjectMap = new Map(
      ((targetProjectsRes.data || []) as Array<Pick<Project, "id" | "name" | "icon" | "color">>).map(
        (project) => [project.id, project]
      )
    );
    const sourceNoteMap = new Map(
      ((sourceNotesRes.data || []) as Array<Pick<Note, "id" | "title" | "project_id" | "updated_at">>).map(
        (note) => [note.id, note]
      )
    );

    setOutbound(
      typedOutbound.map((row) => ({
        ...row,
        target_type: toTargetType(row),
        target_note: row.target_note_id ? targetNoteMap.get(row.target_note_id) || null : null,
        target_task: row.target_task_id ? targetTaskMap.get(row.target_task_id) || null : null,
        target_project:
          row.target_project_id ? targetProjectMap.get(row.target_project_id) || null : null,
      }))
    );

    setBacklinks(
      typedBacklinks.map((row) => ({
        ...row,
        target_type: "note",
        source_note: sourceNoteMap.get(row.source_note_id) || null,
      }))
    );

    const relatedMap = new Map<string, RelatedKnowledgeNote>();

    if (projectId) {
      let sameProjectQuery = supabase
        .from("notes")
        .select("id, title, project_id, updated_at")
        .eq("project_id", projectId)
        .neq("id", noteId)
        .order("updated_at", { ascending: false })
        .limit(8);

      sameProjectQuery = teamId
        ? sameProjectQuery.eq("team_id", teamId)
        : sameProjectQuery.is("team_id", null);

      const { data } = await sameProjectQuery;
      ((data || []) as Array<Pick<Note, "id" | "title" | "project_id" | "updated_at">>).forEach(
        (note) => {
          relatedMap.set(note.id, {
            id: note.id,
            title: note.title,
            project_id: note.project_id,
            updated_at: note.updated_at,
            reasons: ["Same project"],
          });
        }
      );
    }

    if (tagIds.length > 0) {
      const { data: taggedRows } = await supabase
        .from("note_tags")
        .select("note_id, tag_id")
        .in("tag_id", tagIds);

      const counts = new Map<string, number>();
      for (const row of (taggedRows || []) as Array<{ note_id: string; tag_id: string }>) {
        if (row.note_id === noteId) continue;
        counts.set(row.note_id, (counts.get(row.note_id) || 0) + 1);
      }

      const noteIds = Array.from(counts.keys()).slice(0, 12);
      if (noteIds.length > 0) {
        let taggedNotesQuery = supabase
          .from("notes")
          .select("id, title, project_id, updated_at")
          .in("id", noteIds);

        taggedNotesQuery = teamId
          ? taggedNotesQuery.eq("team_id", teamId)
          : taggedNotesQuery.is("team_id", null);

        const { data } = await taggedNotesQuery;
        ((data || []) as Array<Pick<Note, "id" | "title" | "project_id" | "updated_at">>).forEach(
          (note) => {
            const existing = relatedMap.get(note.id);
            const sharedReason =
              counts.get(note.id) && counts.get(note.id)! > 1 ? "Shared tags" : "Shared tag";

            if (existing) {
              if (!existing.reasons.includes(sharedReason)) {
                existing.reasons.push(sharedReason);
              }
              relatedMap.set(note.id, existing);
              return;
            }

            relatedMap.set(note.id, {
              id: note.id,
              title: note.title,
              project_id: note.project_id,
              updated_at: note.updated_at,
              reasons: [sharedReason],
            });
          }
        );
      }
    }

    setRelatedNotes(
      Array.from(relatedMap.values())
        .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())
        .slice(0, 8)
    );

    setLoading(false);
  }, [initialized, noteId, projectId, supabase, tagIds, teamId]);

  useEffect(() => {
    const timer = window.setTimeout(() => {
      void fetchKnowledge();
    }, 0);

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

  async function createRelation(input: {
    targetType: NoteRelation["target_type"];
    targetId: string;
  }) {
    const currentUserId = await requireCurrentUserId();
    if (!currentUserId) throw new Error("Not authenticated");

    const payload = {
      user_id: currentUserId,
      team_id: teamId,
      source_note_id: noteId,
      target_note_id: input.targetType === "note" ? input.targetId : null,
      target_task_id: input.targetType === "task" ? input.targetId : null,
      target_project_id: input.targetType === "project" ? input.targetId : null,
    };

    const { error } = await supabase.from("note_relations").insert(payload);
    if (error) throw error;
    await fetchKnowledge();
  }

  async function deleteRelation(id: string) {
    const { error } = await supabase.from("note_relations").delete().eq("id", id);
    if (error) throw error;
    await fetchKnowledge();
  }

  return {
    outbound,
    backlinks,
    relatedNotes,
    loading,
    createRelation,
    deleteRelation,
    refetch: fetchKnowledge,
  };
}
