"use client";

// Team-management data layer. Serves EITHER the local mock (when the Shell isn't
// configured — the polished demo keeps working) OR the live be-crm data door
// (crm.team.*), behind one interface so the component is mode-agnostic.
//
// Live contract (be-crm, multi-role):
//   query  crm.team.member.search { perPage }        -> { items: MemberDTO[], meta }
//   query  crm.team.role.list     { includeEmpty }   -> { items: RoleDTO[], meta }
//   query  crm.team.invitation.list { status }       -> { items: InvitationDTO[], meta }
//   cmd    crm.team.member.setRoles { id, roleIds }
//   cmd    crm.team.member.update  { id, jobTitle?, roleIds? }
//   cmd    crm.team.member.remove  { id }
//   cmd    crm.team.invitation.create/resend/revoke
//   cmd    crm.team.role.addPermission/removePermission { id, permissionId }

import { useCallback, useMemo, useState } from "react";
import { useAppShell, useAuth, useQuery } from "@abe-kap/appshell-sdk/react";
import { isShellConfigured } from "./appshell";
import {
  members as seedMembers, roles as seedRoles, pendingInvites as seedInvites,
  type Member as SeedMember, type Role as SeedRole, type Invite as SeedInvite,
} from "@/components/dashboard/team-data";

/* ---- UI-facing shapes (multi-role; superset of the mock) ---------------- */

export type UiStatus = "active" | "away" | "offline";

export interface UiRole {
  id: string; slug: string; name: string; color: string;
  description: string; system: boolean; isOwner: boolean;
  perms: string[]; attire: string[]; overlap: string; memberCount?: number;
}
export interface UiMember {
  id: string; principalId?: string; name: string; initials: string; email: string;
  title: string; roleIds: string[]; gradient: string; status: UiStatus;
  lastActive: string; deals: number; joined: string; isOwner: boolean; isYou: boolean;
}
export interface UiInvite {
  id: string; email: string; roleIds: string[]; invitedBy: string; sentAt: string;
  /** Accept token for the invite link (pending invites only; no email delivery yet). */
  token?: string;
}

export interface TeamData {
  live: boolean; loading: boolean; error: string | null;
  members: UiMember[]; roles: UiRole[]; invites: UiInvite[];
  setMemberRoles: (id: string, roleIds: string[]) => Promise<void>;
  updateMember: (id: string, patch: { title?: string; roleIds?: string[] }) => Promise<void>;
  removeMember: (id: string) => Promise<void>;
  /** Create an invite. be-crm emails the invitee automatically. */
  invite: (email: string, roleIds: string[]) => Promise<void>;
  /** Resend an invite (regenerates the token + re-emails it). */
  resendInvite: (id: string) => Promise<void>;
  revokeInvite: (id: string) => Promise<void>;
  setPermission: (roleId: string, permId: string, granted: boolean) => Promise<void>;
  refetch: () => void;
}

/* ---- helpers ------------------------------------------------------------ */

const GRADIENTS = [
  "linear-gradient(135deg,#fda913,#fd6d13)", "linear-gradient(135deg,#6366f1,#8b5cf6)",
  "linear-gradient(135deg,#06b6d4,#3b82f6)", "linear-gradient(135deg,#10b981,#059669)",
  "linear-gradient(135deg,#f43f5e,#ec4899)", "linear-gradient(135deg,#f59e0b,#ef4444)",
  "linear-gradient(135deg,#8b5cf6,#d946ef)", "linear-gradient(135deg,#14b8a6,#0ea5e9)",
];
const gradientFor = (id: string) => GRADIENTS[[...id].reduce((a, c) => a + c.charCodeAt(0), 0) % GRADIENTS.length];
const initialsOf = (name: string) =>
  name.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0]).join("").toUpperCase() || "?";

/* ---- be-crm DTO types (subset used here) -------------------------------- */

interface RoleRef { id: string; slug: string; name: string; color: string | null; isSystem: boolean; isOwnerRole: boolean }
interface MemberDTO { id: string; principalId: string; jobTitle: string | null; joinedAt: string; status: "active" | "deactivated"; roles: RoleRef[]; openDeals: number; email?: string | null; firstName?: string | null; lastName?: string | null; displayName?: string | null }
interface RoleDTO { id: string; slug: string; name: string; description: string | null; color: string | null; isSystem: boolean; isOwnerRole: boolean; permissions: string[]; memberCount: number }
interface InvitationDTO { id: string; email: string; roles: RoleRef[]; invitedBy: string; createdAt: string; token?: string }

const relTime = (iso: string): string => {
  const then = Date.parse(iso);
  if (Number.isNaN(then)) return iso;
  return new Date(then).toLocaleDateString(undefined, { day: "numeric", month: "short", year: "numeric" });
};

/* ---- mock → UI ---------------------------------------------------------- */

const mockRole = (r: SeedRole): UiRole => ({
  id: r.id, slug: r.id, name: r.name, color: r.color, description: r.description,
  system: r.system, isOwner: r.id === "owner", perms: r.perms, attire: r.attire, overlap: r.overlap,
});
const mockMember = (m: SeedMember): UiMember => ({
  id: m.id, name: m.name, initials: m.initials, email: m.email, title: m.title,
  roleIds: [m.roleId], gradient: m.gradient, status: m.status, lastActive: m.lastActive,
  deals: m.deals, joined: m.joined, isOwner: m.roleId === "owner", isYou: m.roleId === "owner",
});
const mockInvite = (i: SeedInvite): UiInvite => ({
  id: i.id, email: i.email, roleIds: [i.roleId], invitedBy: i.invitedBy, sentAt: i.sentAt,
});

/* ======================================================================== */
/* Mock implementation (no Shell configured)                                */
/* ======================================================================== */

function useMockTeam(): TeamData {
  const [members, setMembers] = useState<UiMember[]>(() => seedMembers.map(mockMember));
  const [roles, setRoles] = useState<UiRole[]>(() => seedRoles.map(mockRole));
  const [invites, setInvites] = useState<UiInvite[]>(() => seedInvites.map(mockInvite));

  const setMemberRoles = useCallback(async (id: string, roleIds: string[]) => {
    setMembers((l) => l.map((m) => (m.id === id ? { ...m, roleIds } : m)));
  }, []);
  const updateMember = useCallback(async (id: string, patch: { title?: string; roleIds?: string[] }) => {
    setMembers((l) => l.map((m) => (m.id === id
      ? { ...m, ...(patch.title !== undefined ? { title: patch.title } : {}), ...(patch.roleIds ? { roleIds: patch.roleIds } : {}) }
      : m)));
  }, []);
  const removeMember = useCallback(async (id: string) => { setMembers((l) => l.filter((m) => m.id !== id)); }, []);
  const invite = useCallback(async (email: string, roleIds: string[]) => {
    setInvites((l) => [{ id: `inv_${Date.now()}`, email, roleIds, invitedBy: "James Carter", sentAt: "Just now" }, ...l]);
  }, []);
  const resendInvite = useCallback(async () => {}, []);
  const revokeInvite = useCallback(async (id: string) => { setInvites((l) => l.filter((i) => i.id !== id)); }, []);
  const setPermission = useCallback(async (roleId: string, permId: string, granted: boolean) => {
    setRoles((l) => l.map((r) => (r.id !== roleId ? r : {
      ...r, perms: granted ? [...new Set([...r.perms, permId])] : r.perms.filter((p) => p !== permId),
    })));
  }, []);

  return { live: false, loading: false, error: null, members, roles, invites,
    setMemberRoles, updateMember, removeMember, invite, resendInvite, revokeInvite, setPermission, refetch: () => {} };
}

/* ======================================================================== */
/* Live implementation (be-crm data door)                                   */
/* ======================================================================== */

function useLiveTeam(): TeamData {
  const { sdk } = useAppShell();
  const { user } = useAuth();
  const meId = user?.id;

  const membersQ = useQuery<{ items: MemberDTO[] }>("crm.team.member.search", { perPage: 100 });
  const rolesQ = useQuery<{ items: RoleDTO[] }>("crm.team.role.list", { includeEmpty: true });
  const invitesQ = useQuery<{ items: InvitationDTO[] }>("crm.team.invitation.list", { status: "pending" });

  const refetch = useCallback(() => { membersQ.refetch(); rolesQ.refetch(); invitesQ.refetch(); }, [membersQ, rolesQ, invitesQ]);
  const cmd = useCallback(async (action: string, variables: Record<string, unknown>) => {
    await sdk.command(action, variables);
    refetch();
  }, [sdk, refetch]);

  const roles: UiRole[] = useMemo(() => (rolesQ.data?.items ?? []).map((r) => ({
    id: r.id, slug: r.slug, name: r.name, color: r.color ?? "var(--text-2)",
    description: r.description ?? "", system: r.isSystem, isOwner: r.isOwnerRole,
    perms: r.permissions, attire: [], overlap: "", memberCount: r.memberCount,
  })), [rolesQ.data]);

  const members: UiMember[] = useMemo(() => (membersQ.data?.items ?? []).map((m) => {
    const isYou = !!meId && m.principalId === meId;
    // Prefer the member's real name/email from their CRM registration; fall back to the
    // ACE (for the current user), then job title, then a short principal id.
    const name = m.displayName?.trim()
      || (isYou && user?.displayName)
      || m.jobTitle?.trim()
      || `Member ${m.principalId.replace(/^pp_/, "").slice(0, 6)}`;
    const email = m.email || (isYou ? user?.email : undefined) || "";
    return {
      id: m.id, principalId: m.principalId, name, initials: initialsOf(name),
      email,
      title: m.jobTitle ?? "", roleIds: m.roles.map((r) => r.id), gradient: gradientFor(m.id),
      status: (m.status === "active" ? "active" : "offline") as UiStatus,
      lastActive: m.status === "active" ? "Active" : "—",
      deals: m.openDeals, joined: relTime(m.joinedAt),
      isOwner: m.roles.some((r) => r.isOwnerRole), isYou,
    };
  }), [membersQ.data, meId, user]);

  const invites: UiInvite[] = useMemo(() => (invitesQ.data?.items ?? []).map((i) => ({
    id: i.id, email: i.email, roleIds: i.roles.map((r) => r.id),
    invitedBy: i.invitedBy, sentAt: relTime(i.createdAt), token: i.token,
  })), [invitesQ.data]);

  return {
    live: true,
    loading: membersQ.loading || rolesQ.loading || invitesQ.loading,
    error: (membersQ.error ?? rolesQ.error ?? invitesQ.error)?.message ?? null,
    members, roles, invites,
    setMemberRoles: (id, roleIds) => cmd("crm.team.member.setRoles", { id, roleIds }),
    updateMember: (id, patch) => cmd("crm.team.member.update", {
      id, ...(patch.title !== undefined ? { jobTitle: patch.title } : {}), ...(patch.roleIds ? { roleIds: patch.roleIds } : {}),
    }),
    removeMember: (id) => cmd("crm.team.member.remove", { id }),
    invite: (email, roleIds) => cmd("crm.team.invitation.create", { email, roleIds }),
    resendInvite: (id) => cmd("crm.team.invitation.resend", { id }),
    revokeInvite: (id) => cmd("crm.team.invitation.revoke", { id }),
    setPermission: (roleId, permId, granted) =>
      cmd(granted ? "crm.team.role.addPermission" : "crm.team.role.removePermission", { id: roleId, permissionId: permId }),
    refetch,
  };
}

/* ---- public hook: pick the implementation at module-config time --------- */

const SHELL = isShellConfigured();

export function useTeamData(): TeamData {
  // `SHELL` is constant for the life of the bundle (NEXT_PUBLIC_* is build-time),
  // so the same hook path runs every render — Rules-of-Hooks safe.
  return SHELL ? useLiveTeam() : useMockTeam();
}
