import { useState, useMemo } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import {
  Search, Loader2, Grid3X3, Check, X, Shield, Download, Users,
} from "lucide-react";
import { useToast } from "@/hooks/use-toast";
import { rolesService } from "@/services/roles.service";
import type { Permission, AuthorityMatrixRole, UserAuthorityMatrixUser } from "@/types/role-permission";

const CATEGORY_COLORS: Record<string, string> = {
  exam: "border-violet-400 bg-violet-50 dark:bg-violet-950",
  user: "border-blue-400 bg-blue-50 dark:bg-blue-950",
  entity: "border-emerald-400 bg-emerald-50 dark:bg-emerald-950",
  assignment: "border-amber-400 bg-amber-50 dark:bg-amber-950",
  classroom: "border-rose-400 bg-rose-50 dark:bg-rose-950",
  payment: "border-cyan-400 bg-cyan-50 dark:bg-cyan-950",
  report: "border-orange-400 bg-orange-50 dark:bg-orange-950",
};

const CATEGORY_BADGE: Record<string, string> = {
  exam: "bg-violet-100 text-violet-700",
  user: "bg-blue-100 text-blue-700",
  entity: "bg-emerald-100 text-emerald-700",
  assignment: "bg-amber-100 text-amber-700",
  classroom: "bg-rose-100 text-rose-700",
  payment: "bg-cyan-100 text-cyan-700",
  report: "bg-orange-100 text-orange-700",
};

function initials(name: string) {
  return name.split(" ").map(w => w[0]).join("").toUpperCase().slice(0, 2);
}

function filterCategories(categories: Record<string, Permission[]>, query: string) {
  if (!query) return categories;
  const q = query.toLowerCase();
  const result: Record<string, Permission[]> = {};
  for (const [cat, perms] of Object.entries(categories)) {
    const filtered = perms.filter(p =>
      p.name.toLowerCase().includes(q) || p.code.toLowerCase().includes(q) || cat.toLowerCase().includes(q),
    );
    if (filtered.length > 0) result[cat] = filtered;
  }
  return result;
}

// ── Shared matrix grid component ──
function MatrixGrid<T extends { id: number; granted_permission_ids: number[] }>({
  columns, categories, search, renderColumnHeader, onCellClick, pendingCells,
}: {
  columns: T[];
  categories: Record<string, Permission[]>;
  search: string;
  renderColumnHeader: (col: T) => React.ReactNode;
  onCellClick?: (colId: number, permId: number) => void;
  pendingCells: Set<string>;
}) {
  const filtered = useMemo(() => filterCategories(categories, search), [categories, search]);

  if (columns.length === 0) {
    return <Card><CardContent className="py-16 text-center text-muted-foreground">No data available.</CardContent></Card>;
  }
  if (Object.keys(filtered).length === 0) {
    return <Card><CardContent className="py-16 text-center text-muted-foreground">No permissions match your search.</CardContent></Card>;
  }

  return (
    <div className="space-y-6">
      {Object.entries(filtered).map(([cat, perms]) => (
        <Card key={cat} className={`border-l-4 ${CATEGORY_COLORS[cat.toLowerCase()] ?? "border-l-gray-400 bg-gray-50 dark:bg-gray-950"}`}>
          <CardHeader className="pb-2">
            <CardTitle className="text-sm font-semibold flex items-center gap-2">
              <Badge className={`${CATEGORY_BADGE[cat.toLowerCase()] ?? "bg-gray-100 text-gray-700"} font-medium text-xs`}>{cat}</Badge>
              <span className="text-muted-foreground font-normal">{perms.length} permission{perms.length !== 1 ? "s" : ""}</span>
            </CardTitle>
          </CardHeader>
          <CardContent className="overflow-x-auto pb-4">
            <table className="w-full text-sm">
              <thead>
                <tr>
                  <th className="text-left font-medium text-muted-foreground pb-2 pr-4 min-w-[200px] sticky left-0 bg-card z-10">Permission</th>
                  {columns.map(col => (
                    <th key={col.id} className="text-center font-medium text-muted-foreground pb-2 px-2 min-w-[100px]">
                      {renderColumnHeader(col)}
                    </th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {perms.map((p, idx) => (
                  <tr key={p.id} className={idx % 2 === 0 ? "" : "bg-muted/30"}>
                    <td className="py-1.5 pr-4 sticky left-0 bg-card z-10">
                      <Tooltip>
                        <TooltipTrigger asChild>
                          <div className="flex items-center gap-2 cursor-default">
                            <code className="text-[10px] font-mono text-muted-foreground bg-muted px-1 rounded">{p.code}</code>
                            <span className="text-xs font-medium truncate max-w-[140px]">{p.name}</span>
                          </div>
                        </TooltipTrigger>
                        <TooltipContent side="right" className="max-w-xs">
                          <p className="font-medium">{p.name}</p>
                          <p className="text-xs text-muted-foreground">{p.description || "No description"}</p>
                        </TooltipContent>
                      </Tooltip>
                    </td>
                    {columns.map(col => {
                      const granted = col.granted_permission_ids.includes(p.id);
                      const cellKey = `${col.id}-${p.id}`;
                      const pending = pendingCells.has(cellKey);
                      return (
                        <td key={col.id} className="text-center py-1.5 px-2">
                          <Tooltip>
                            <TooltipTrigger asChild>
                              <button
                                onClick={() => onCellClick?.(col.id, p.id)}
                                disabled={pending || !onCellClick}
                                className={`
                                  inline-flex items-center justify-center h-7 w-7 rounded-md border transition-all duration-150
                                  ${pending ? "opacity-50 cursor-wait" : onCellClick ? "cursor-pointer hover:scale-110" : "cursor-default"}
                                  ${granted
                                    ? "bg-emerald-500/20 border-emerald-500/40 hover:bg-emerald-500/30"
                                    : "bg-muted/50 border-border hover:bg-muted hover:border-muted-foreground/30"
                                  }
                                `}
                              >
                                {pending ? (
                                  <Loader2 className="h-3 w-3 animate-spin text-muted-foreground" />
                                ) : granted ? (
                                  <Check className="h-3.5 w-3.5 text-emerald-600" />
                                ) : (
                                  <X className="h-3 w-3 text-muted-foreground/30" />
                                )}
                              </button>
                            </TooltipTrigger>
                            <TooltipContent>
                              {granted ? "Granted" : "Not granted"}: <strong>{p.code}</strong>
                            </TooltipContent>
                          </Tooltip>
                        </td>
                      );
                    })}
                  </tr>
                ))}
              </tbody>
            </table>
          </CardContent>
        </Card>
      ))}
    </div>
  );
}

export default function AuthorityMatrix() {
  const { toast } = useToast();
  const qc = useQueryClient();
  const [search, setSearch] = useState("");
  const [tab, setTab] = useState("roles");
  const [pendingCells, setPendingCells] = useState<Set<string>>(new Set());

  const roleMatrixQ = useQuery({ queryKey: ["authority-matrix"], queryFn: rolesService.getAuthorityMatrix });
  const userMatrixQ = useQuery({ queryKey: ["user-authority-matrix"], queryFn: rolesService.getUserAuthorityMatrix, enabled: tab === "users" });

  const toggleMut = useMutation({
    mutationFn: ({ roleId, permId }: { roleId: number; permId: number }) =>
      rolesService.toggleMatrixCell(roleId, permId),
    onMutate: ({ roleId, permId }) => {
      setPendingCells(prev => new Set(prev).add(`${roleId}-${permId}`));
    },
    onSuccess: (result) => {
      qc.invalidateQueries({ queryKey: ["authority-matrix"] });
      qc.invalidateQueries({ queryKey: ["user-authority-matrix"] });
      setPendingCells(prev => { const s = new Set(prev); s.delete(`${result.role_id}-${result.permission_id}`); return s; });
    },
    onError: (_err, { roleId, permId }) => {
      setPendingCells(prev => { const s = new Set(prev); s.delete(`${roleId}-${permId}`); return s; });
      toast({ title: "Failed to toggle", variant: "destructive" });
    },
  });

  const roles: AuthorityMatrixRole[] = roleMatrixQ.data?.roles ?? [];
  const roleCategories: Record<string, Permission[]> = roleMatrixQ.data?.categories ?? {};
  const matrixUsers: UserAuthorityMatrixUser[] = userMatrixQ.data?.users ?? [];
  const userCategories: Record<string, Permission[]> = userMatrixQ.data?.categories ?? {};

  const totalPerms = Object.values(roleCategories).reduce((s, p) => s + p.length, 0);
  const totalGranted = roles.reduce((s, r) => s + r.granted_permission_ids.length, 0);
  const totalCells = roles.length * totalPerms;
  const coveragePercent = totalCells > 0 ? Math.round((totalGranted / totalCells) * 100) : 0;

  const handleExportCsv = () => {
    const cats = tab === "roles" ? roleCategories : userCategories;
    const allPerms = Object.values(cats).flat();
    if (tab === "roles") {
      const header = ["Permission Code", "Permission Name", "Category", ...roles.map(r => r.name)];
      const rows = allPerms.map(p => {
        const cells = roles.map(r => r.granted_permission_ids.includes(p.id) ? "Yes" : "No");
        return [p.code, p.name, p.category, ...cells];
      });
      downloadCsv([header, ...rows], "authority-matrix-roles.csv");
    } else {
      const header = ["Permission Code", "Permission Name", "Category", ...matrixUsers.map(u => u.name)];
      const rows = allPerms.map(p => {
        const cells = matrixUsers.map(u => u.granted_permission_ids.includes(p.id) ? "Yes" : "No");
        return [p.code, p.name, p.category, ...cells];
      });
      downloadCsv([header, ...rows], "authority-matrix-users.csv");
    }
  };

  const loading = roleMatrixQ.isLoading || (tab === "users" && userMatrixQ.isLoading);

  if (roleMatrixQ.isLoading) {
    return <div className="flex items-center justify-center min-h-[400px]"><Loader2 className="h-8 w-8 animate-spin text-primary" /></div>;
  }

  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex flex-wrap items-center justify-between gap-4">
        <div>
          <h1 className="text-2xl font-bold flex items-center gap-2">
            <Grid3X3 className="h-7 w-7 text-primary" />
            Authority Matrix
          </h1>
          <p className="text-muted-foreground">Visual overview of permission assignments across roles and users.</p>
        </div>
        <Button variant="outline" onClick={handleExportCsv}><Download className="mr-2 h-4 w-4" /> Export CSV</Button>
      </div>

      {/* Summary */}
      <div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
        <Card>
          <CardContent className="pt-5 pb-4 flex items-center gap-3">
            <div className="h-10 w-10 rounded-lg bg-primary/10 flex items-center justify-center"><Shield className="h-5 w-5 text-primary" /></div>
            <div><p className="text-xl font-bold">{roles.length}</p><p className="text-xs text-muted-foreground">Roles</p></div>
          </CardContent>
        </Card>
        <Card>
          <CardContent className="pt-5 pb-4 flex items-center gap-3">
            <div className="h-10 w-10 rounded-lg bg-amber-500/10 flex items-center justify-center"><Grid3X3 className="h-5 w-5 text-amber-500" /></div>
            <div><p className="text-xl font-bold">{totalPerms}</p><p className="text-xs text-muted-foreground">Permissions</p></div>
          </CardContent>
        </Card>
        <Card>
          <CardContent className="pt-5 pb-4 flex items-center gap-3">
            <div className="h-10 w-10 rounded-lg bg-emerald-500/10 flex items-center justify-center"><Check className="h-5 w-5 text-emerald-500" /></div>
            <div><p className="text-xl font-bold">{totalGranted}</p><p className="text-xs text-muted-foreground">Granted</p></div>
          </CardContent>
        </Card>
        <Card>
          <CardContent className="pt-5 pb-4 flex items-center gap-3">
            <div className="h-10 w-10 rounded-lg bg-blue-500/10 flex items-center justify-center">
              <span className="text-sm font-bold text-blue-500">{coveragePercent}%</span>
            </div>
            <div><p className="text-xl font-bold">Coverage</p><p className="text-xs text-muted-foreground">{totalGranted}/{totalCells} cells</p></div>
          </CardContent>
        </Card>
      </div>

      {/* Tabs & Search */}
      <Tabs value={tab} onValueChange={setTab}>
        <div className="flex flex-wrap items-center justify-between gap-3">
          <TabsList>
            <TabsTrigger value="roles" className="gap-1.5"><Shield className="h-4 w-4" /> Roles ({roles.length})</TabsTrigger>
            <TabsTrigger value="users" className="gap-1.5"><Users className="h-4 w-4" /> Users ({matrixUsers.length || "..."})</TabsTrigger>
          </TabsList>
          <div className="relative max-w-xs w-full">
            <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
            <Input placeholder="Filter permissions..." className="pl-9" value={search} onChange={e => setSearch(e.target.value)} />
          </div>
        </div>

        {/* Legend */}
        <div className="flex flex-wrap items-center gap-4 text-xs text-muted-foreground mt-4">
          <span className="font-medium text-foreground">Legend:</span>
          <span className="flex items-center gap-1.5">
            <span className="inline-flex h-5 w-5 rounded bg-emerald-500/20 border border-emerald-500/40 items-center justify-center"><Check className="h-3 w-3 text-emerald-600" /></span>
            Granted
          </span>
          <span className="flex items-center gap-1.5">
            <span className="inline-flex h-5 w-5 rounded bg-muted border border-border items-center justify-center"><X className="h-3 w-3 text-muted-foreground/40" /></span>
            Not granted
          </span>
          {tab === "roles" && <span className="ml-auto">Click any cell to toggle</span>}
          {tab === "users" && <span className="ml-auto text-muted-foreground/70">User permissions are derived from roles (read-only)</span>}
        </div>

        {/* ── Roles Matrix Tab ── */}
        <TabsContent value="roles" className="mt-4">
          <MatrixGrid
            columns={roles}
            categories={roleCategories}
            search={search}
            pendingCells={pendingCells}
            onCellClick={(colId, permId) => toggleMut.mutate({ roleId: colId, permId })}
            renderColumnHeader={(r) => (
              <div className="flex flex-col items-center gap-0.5">
                <Shield className="h-3.5 w-3.5 text-primary/60" />
                <span className="text-xs leading-tight">{r.name}</span>
              </div>
            )}
          />
        </TabsContent>

        {/* ── Users Matrix Tab ── */}
        <TabsContent value="users" className="mt-4">
          {userMatrixQ.isLoading ? (
            <div className="flex items-center justify-center py-16"><Loader2 className="h-8 w-8 animate-spin text-primary" /></div>
          ) : matrixUsers.length === 0 ? (
            <Card><CardContent className="py-16 text-center text-muted-foreground">No users with roles assigned. Assign roles in the User Roles page first.</CardContent></Card>
          ) : (
            <MatrixGrid
              columns={matrixUsers}
              categories={userCategories}
              search={search}
              pendingCells={new Set()}
              renderColumnHeader={(u) => (
                <div className="flex flex-col items-center gap-0.5">
                  <Avatar className="h-6 w-6">
                    <AvatarFallback className="text-[9px] bg-primary/10 text-primary font-medium">{initials(u.name)}</AvatarFallback>
                  </Avatar>
                  <span className="text-[10px] leading-tight max-w-[80px] truncate">{u.name}</span>
                  <span className="text-[9px] text-muted-foreground">{u.roles.length} role{u.roles.length !== 1 ? "s" : ""}</span>
                </div>
              )}
            />
          )}
        </TabsContent>
      </Tabs>
    </div>
  );
}

function downloadCsv(rows: string[][], filename: string) {
  const csv = rows.map(r => r.map(c => `"${String(c).replace(/"/g, '""')}"`).join(",")).join("\n");
  const blob = new Blob([csv], { type: "text/csv" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url; a.download = filename; a.click();
  URL.revokeObjectURL(url);
}
