"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { createClient, getBrowserUser } from "@/lib/supabase/client";
import { useWorkspaceStore } from "@/stores/workspace-store";
import { logTeamAudit } from "@/lib/team-audit";
import type { Profile, Team, TeamInvitation, TeamMember, TeamRole } from "@/types";

function slugify(value: string) {
  return value
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "")
    .slice(0, 40);
}

function createTeamSlug(name: string) {
  const base = slugify(name) || "team";
  return `${base}-${Math.random().toString(36).slice(2, 8)}`;
}

type TeamMembersByTeam = Record<string, TeamMember[]>;
type TeamInvitationsByTeam = Record<string, TeamInvitation[]>;

export function useTeams() {
  const [teams, setTeams] = useState<Team[]>([]);
  const [membersByTeam, setMembersByTeam] = useState<TeamMembersByTeam>({});
  const [invitationsByTeam, setInvitationsByTeam] =
    useState<TeamInvitationsByTeam>({});
  const [incomingInvitations, setIncomingInvitations] = useState<
    TeamInvitation[]
  >([]);
  const [loading, setLoading] = useState(true);
  const [userId, setUserId] = useState<string | null>(null);
  const [userEmail, setUserEmail] = useState<string | null>(null);

  const supabase = createClient();
  const activeTeamId = useWorkspaceStore((state) => state.activeTeamId);
  const setActiveTeamId = useWorkspaceStore((state) => state.setActiveTeamId);
  const hasExplicitChoice = useWorkspaceStore((state) => state.hasExplicitChoice);

  useEffect(() => {
    void getBrowserUser()
      .then((user) => {
        setUserId(user?.id ?? null);
        setUserEmail(user?.email?.toLowerCase() ?? null);
      })
      .catch(() => {
        setUserId(null);
        setUserEmail(null);
      });
  }, []);

  const fetchProfilesByIds = useCallback(
    async (profileIds: string[]) => {
      const uniqueProfileIds = Array.from(new Set(profileIds.filter(Boolean)));

      if (uniqueProfileIds.length === 0) {
        return new Map<string, Profile>();
      }

      const rpcRes = await supabase.rpc("get_accessible_profiles", {
        profile_ids: uniqueProfileIds,
      });

      const rpcError = rpcRes.error as { message?: string } | null;
      const rpcMessage =
        rpcRes.error instanceof Error
          ? rpcRes.error.message
          : typeof rpcError?.message === "string"
            ? rpcError.message
            : "";

      const shouldFallback =
        !!rpcRes.error &&
        /(get_accessible_profiles|Could not find the function|schema cache|does not exist)/i.test(
          rpcMessage
        );

      const directRes =
        shouldFallback
          ? await supabase
              .from("profiles")
              .select("id, email, full_name, avatar_url, username, created_at, updated_at")
              .in("id", uniqueProfileIds)
          : null;

      const data = (rpcRes.data || directRes?.data || []) as Profile[];
      const error = rpcRes.error && !shouldFallback ? rpcRes.error : directRes?.error || null;

      if (error) {
        console.error("Failed to load team profiles", error);
        return new Map<string, Profile>();
      }

      return new Map(data.map((profile) => [profile.id, profile]));
    },
    [supabase]
  );

  const fetchTeams = useCallback(async () => {
    if (!userId) {
      setTeams([]);
      setMembersByTeam({});
      setInvitationsByTeam({});
      setIncomingInvitations([]);
      setLoading(false);
      return;
    }

    setLoading(true);

    try {
      const [teamsRes, incomingRes] = await Promise.all([
        supabase.from("teams").select("*").order("name"),
        supabase
          .from("team_invitations")
          .select("*")
          .eq("status", "pending")
          .order("created_at", { ascending: false }),
      ]);

      if (teamsRes.error) throw teamsRes.error;
      if (incomingRes.error) throw incomingRes.error;

      const nextTeams = (teamsRes.data || []) as Team[];
      const nextIncomingInvitations = ((incomingRes.data || []) as TeamInvitation[])
        .filter((invitation) =>
          userEmail
            ? invitation.email.toLowerCase() === userEmail
            : false
        );

      setTeams(nextTeams);
      setIncomingInvitations(nextIncomingInvitations);

      if (nextTeams.length > 0) {
        if (activeTeamId && !nextTeams.some((team) => team.id === activeTeamId)) {
          // Active team was deleted/left → fall back to first available team
          setActiveTeamId(nextTeams[0].id);
        } else if (!activeTeamId && !hasExplicitChoice) {
          // No stored preference at all (brand-new user) → default to first team
          setActiveTeamId(nextTeams[0].id);
        }
        // If !activeTeamId && hasExplicitChoice → user deliberately chose Personal, respect it
      } else if (activeTeamId) {
        setActiveTeamId(null);
      }

      if (nextTeams.length === 0) {
        setMembersByTeam({});
        setInvitationsByTeam({});
        setLoading(false);
        return;
      }

      const teamIds = nextTeams.map((team) => team.id);
      const [membersRes, invitationsRes] = await Promise.all([
        supabase.from("team_members").select("*").in("team_id", teamIds),
        supabase
          .from("team_invitations")
          .select("*")
          .in("team_id", teamIds)
          .order("created_at", { ascending: false }),
      ]);

      if (membersRes.error) throw membersRes.error;
      if (invitationsRes.error) throw invitationsRes.error;

      const rawMembers = (membersRes.data || []) as TeamMember[];
      const rawInvitations = (invitationsRes.data || []) as TeamInvitation[];

      const profileIds = Array.from(
        new Set([
          ...rawMembers.map((member) => member.user_id),
          ...nextTeams.map((team) => team.owner_id),
        ])
      );

      const profileMap = await fetchProfilesByIds(profileIds);

      const nextMembersByTeam: TeamMembersByTeam = {};
      const nextInvitationsByTeam: TeamInvitationsByTeam = {};

      nextTeams.forEach((team) => {
        const teamMembers = rawMembers.filter((member) => member.team_id === team.id);

        if (!teamMembers.some((member) => member.user_id === team.owner_id)) {
          teamMembers.unshift({
            team_id: team.id,
            user_id: team.owner_id,
            role: "owner",
            title: "Workspace owner",
            joined_at: team.created_at,
            profile: profileMap.get(team.owner_id),
          });
        }

        nextMembersByTeam[team.id] = teamMembers.map((member) => ({
          ...member,
          profile: profileMap.get(member.user_id),
        }));
        nextInvitationsByTeam[team.id] = rawInvitations.filter(
          (invitation) => invitation.team_id === team.id
        );
      });

      setMembersByTeam(nextMembersByTeam);
      setInvitationsByTeam(nextInvitationsByTeam);
    } catch (error) {
      console.error("Failed to load teams", error);
    } finally {
      setLoading(false);
    }
  }, [activeTeamId, fetchProfilesByIds, hasExplicitChoice, setActiveTeamId, supabase, userEmail, userId]);

  useEffect(() => {
    void fetchTeams();
  }, [fetchTeams]);

  const activeTeam = useMemo(
    () => teams.find((team) => team.id === activeTeamId) || null,
    [activeTeamId, teams]
  );

  const activeMembers = useMemo(
    () => (activeTeamId ? membersByTeam[activeTeamId] || [] : []),
    [activeTeamId, membersByTeam]
  );

  const activeInvitations = useMemo(
    () => (activeTeamId ? invitationsByTeam[activeTeamId] || [] : []),
    [activeTeamId, invitationsByTeam]
  );

  const activeRole = useMemo(() => {
    if (!activeTeamId || !userId) return null;
    const membership = (membersByTeam[activeTeamId] || []).find(
      (member) => member.user_id === userId
    );
    return membership?.role || null;
  }, [activeTeamId, membersByTeam, userId]);

  const canManageActiveTeam =
    activeRole === "owner" || activeRole === "admin";

  async function requireCurrentUser() {
    if (userId) {
      return {
        id: userId,
        email: userEmail,
      };
    }

    const user = await getBrowserUser({ force: true });

    if (!user) {
      throw new Error("Not authenticated");
    }

    const normalizedEmail = user.email?.toLowerCase() ?? null;
    setUserId(user.id);
    setUserEmail(normalizedEmail);

    return {
      id: user.id,
      email: normalizedEmail,
    };
  }

  async function createTeam(input: { name: string; description?: string }) {
    const currentUser = await requireCurrentUser();

    const { data, error } = await supabase
      .from("teams")
      .insert({
        owner_id: currentUser.id,
        name: input.name.trim(),
        slug: createTeamSlug(input.name),
        description: input.description?.trim() || "",
      })
      .select()
      .single();

    if (error) throw error;

    const { error: memberError } = await supabase.from("team_members").insert({
      team_id: data.id,
      user_id: currentUser.id,
      role: "owner",
      title: "Workspace owner",
    });

    if (memberError) throw memberError;

    await logTeamAudit(supabase, {
      teamId: data.id,
      actorId: currentUser.id,
      action: "team_created",
      entityType: "team",
      entityId: data.id,
      title: data.name,
      details: {
        description: data.description,
      },
    });

    setActiveTeamId(data.id);
    await fetchTeams();
    return data as Team;
  }

  async function updateTeam(
    teamId: string,
    input: Partial<{ name: string; description: string }>
  ) {
    const currentUser = await requireCurrentUser();
    const payload: Record<string, string> = {};

    if (typeof input.name === "string") {
      payload.name = input.name.trim();
    }
    if (typeof input.description === "string") {
      payload.description = input.description.trim();
    }

    const { error } = await supabase.from("teams").update(payload).eq("id", teamId);
    if (error) throw error;
    await logTeamAudit(supabase, {
      teamId,
      actorId: currentUser.id,
      action: "team_updated",
      entityType: "team",
      entityId: teamId,
      title: payload.name || "Team updated",
      details: payload,
    });
    await fetchTeams();
  }

  async function inviteMember(
    teamId: string,
    input: {
      email: string;
      role: Exclude<TeamRole, "owner">;
      message?: string;
    }
  ) {
    await requireCurrentUser();

    const normalizedEmail = input.email.trim().toLowerCase();
    const existingMember = (membersByTeam[teamId] || []).some(
      (member) => member.profile?.email?.toLowerCase() === normalizedEmail
    );

    if (existingMember) {
      throw new Error("This person is already part of the team.");
    }

    const existingInvitation = (invitationsByTeam[teamId] || []).some(
      (invitation) =>
        invitation.status === "pending" &&
        invitation.email.toLowerCase() === normalizedEmail
    );

    if (existingInvitation) {
      throw new Error("There is already a pending invitation for this email.");
    }

    const res = await fetch("/api/teams/invitations", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        teamId,
        email: normalizedEmail,
        role: input.role,
        message: input.message || "",
      }),
    });

    const result = await res.json();

    if (!res.ok) {
      throw new Error(result.error || "Invitation could not be sent.");
    }

    await fetchTeams();
    return result as {
      invitation: TeamInvitation;
      inviteUrl: string;
      delivery: "email_sent" | "stored_only";
    };
  }

  async function acceptInvitation(invitationId: string) {
    const currentUser = await requireCurrentUser();

    const invitation = incomingInvitations.find(
      (item) => item.id === invitationId
    );

    if (!invitation) throw new Error("Invitation not found.");

    const { error: memberError } = await supabase.from("team_members").insert({
      team_id: invitation.team_id,
      user_id: currentUser.id,
      role: invitation.role,
      title: "",
    });

    if (memberError && memberError.code !== "23505") {
      throw memberError;
    }

    const { error: inviteError } = await supabase
      .from("team_invitations")
      .update({ status: "accepted" })
      .eq("id", invitationId);

    if (inviteError) throw inviteError;

    await logTeamAudit(supabase, {
      teamId: invitation.team_id,
      actorId: currentUser.id,
      action: "invite_accepted",
      entityType: "invite",
      entityId: invitation.id,
      title: invitation.email,
      details: { role: invitation.role },
    });

    await logTeamAudit(supabase, {
      teamId: invitation.team_id,
      actorId: currentUser.id,
      action: "member_joined",
      entityType: "member",
      entityId: currentUser.id,
      title: invitation.email,
      details: { role: invitation.role },
    });

    setActiveTeamId(invitation.team_id);
    await fetchTeams();
  }

  async function declineInvitation(invitationId: string) {
    const currentUser = await requireCurrentUser();
    const invitation = incomingInvitations.find((item) => item.id === invitationId) || null;
    const { error } = await supabase
      .from("team_invitations")
      .update({ status: "revoked" })
      .eq("id", invitationId);

    if (error) throw error;
    await logTeamAudit(supabase, {
      teamId: invitation?.team_id,
      actorId: currentUser.id,
      action: "invite_declined",
      entityType: "invite",
      entityId: invitationId,
      title: invitation?.email || "Invitation declined",
    });
    await fetchTeams();
  }

  async function revokeInvitation(invitationId: string) {
    const currentUser = await requireCurrentUser();
    const invitation =
      Object.values(invitationsByTeam)
        .flat()
        .find((item) => item.id === invitationId) || null;

    const { error } = await supabase
      .from("team_invitations")
      .update({ status: "revoked" })
      .eq("id", invitationId);

    if (error) throw error;
    await logTeamAudit(supabase, {
      teamId: invitation?.team_id,
      actorId: currentUser.id,
      action: "invite_revoked",
      entityType: "invite",
      entityId: invitationId,
      title: invitation?.email || "Invitation revoked",
      details: invitation ? { role: invitation.role } : {},
    });
    await fetchTeams();
  }

  async function updateMemberRole(
    teamId: string,
    memberId: string,
    role: Exclude<TeamRole, "owner">
  ) {
    const currentUser = await requireCurrentUser();
    const existing = (membersByTeam[teamId] || []).find(
      (member) => member.user_id === memberId
    );

    const { error } = await supabase
      .from("team_members")
      .update({ role })
      .eq("team_id", teamId)
      .eq("user_id", memberId);

    if (error) throw error;
    await logTeamAudit(supabase, {
      teamId,
      actorId: currentUser.id,
      action: "member_role_changed",
      entityType: "member",
      entityId: memberId,
      title:
        existing?.profile?.full_name || existing?.profile?.email || "Member role changed",
      details: {
        from_role: existing?.role,
        to_role: role,
      },
    });
    await fetchTeams();
  }

  async function updateMemberTitle(teamId: string, memberId: string, title: string) {
    const currentUser = await requireCurrentUser();
    const existing = (membersByTeam[teamId] || []).find(
      (member) => member.user_id === memberId
    );

    const { error } = await supabase
      .from("team_members")
      .update({ title: title.trim() })
      .eq("team_id", teamId)
      .eq("user_id", memberId);

    if (error) throw error;
    await logTeamAudit(supabase, {
      teamId,
      actorId: currentUser.id,
      action: "member_title_changed",
      entityType: "member",
      entityId: memberId,
      title:
        existing?.profile?.full_name || existing?.profile?.email || "Member title changed",
      details: {
        from_title: existing?.title || "",
        to_title: title.trim(),
      },
    });
    await fetchTeams();
  }

  async function removeMember(teamId: string, memberId: string) {
    const currentUser = await requireCurrentUser();
    const existing = (membersByTeam[teamId] || []).find(
      (member) => member.user_id === memberId
    );

    const { error } = await supabase
      .from("team_members")
      .delete()
      .eq("team_id", teamId)
      .eq("user_id", memberId);

    if (error) throw error;

    await logTeamAudit(supabase, {
      teamId,
      actorId: currentUser.id,
      action: memberId === currentUser.id ? "member_left" : "member_removed",
      entityType: "member",
      entityId: memberId,
      title:
        existing?.profile?.full_name || existing?.profile?.email || "Member removed",
      details: {
        role: existing?.role,
      },
    });

    if (memberId === currentUser.id && activeTeamId === teamId) {
      setActiveTeamId(null);
    }

    await fetchTeams();
  }

  return {
    teams,
    membersByTeam,
    invitationsByTeam,
    incomingInvitations,
    loading,
    userId,
    userEmail,
    activeTeam,
    activeMembers,
    activeInvitations,
    activeRole,
    canManageActiveTeam,
    createTeam,
    updateTeam,
    inviteMember,
    acceptInvitation,
    declineInvitation,
    revokeInvitation,
    updateMemberRole,
    updateMemberTitle,
    removeMember,
    refetch: fetchTeams,
  };
}
