import { computeEffectiveAccess } from "../../entities/access/computeEffectiveAccess";
import type { EffectiveAccessResult, ServiceAccessException, ServiceGrant } from "../../entities/access/types";
import type { Client } from "../../entities/client/types";
import type { Invite } from "../../entities/invite/types";
import { getServiceLaunchLink } from "../../entities/service/links";
import type { LauncherServiceView, Service } from "../../entities/service/types";
import type { SyncStatus } from "../../entities/sync/types";
import type {
  ClientGroup,
  ClientMembership,
  ClientMembershipRole,
  LauncherGlobalRole,
  LauncherUser,
} from "../../entities/user/types";
import { resolveLauncherRole, resolvePermissions, type LauncherPermissions } from "../lib/permissions";
import {
  mockAuditEvents,
  mockClients,
  mockExceptions,
  mockGrants,
  mockGroups,
  mockInvites,
  mockMemberships,
  mockServices,
  mockSyncStatuses,
  mockUsers,
} from "./mockData";

export interface AuthentikClaimsMock {
  sub: string;
  email: string;
  name: string;
  avatarUrl?: string | null;
  groups: string[];
  activeClientId: string;
}

export interface MeResponse {
  user: Pick<LauncherUser, "id" | "authentikUserId" | "name" | "email" | "avatarUrl">;
  launcherRole: LauncherGlobalRole;
  memberships: Array<{
    clientId: string;
    clientName: string;
    role: ClientMembershipRole;
    status: ClientMembership["status"];
  }>;
  activeClientId: string;
  permissions: LauncherPermissions;
  mockAuthentikClaims: AuthentikClaimsMock;
}

export interface LauncherData {
  clients: Client[];
  users: LauncherUser[];
  memberships: ClientMembership[];
  groups: ClientGroup[];
  services: Service[];
  grants: ServiceGrant[];
  exceptions: ServiceAccessException[];
  invites: Invite[];
  syncStatuses: SyncStatus[];
  auditEvents: typeof mockAuditEvents;
}

export interface ProfileOption {
  userId: string;
  label: string;
  description: string;
  defaultClientId: string;
}

export interface AccessMatrixCell {
  userId: string;
  serviceId: string;
  effectiveAccess: EffectiveAccessResult;
}

export interface AccessMatrix {
  client: Client;
  users: LauncherUser[];
  groups: ClientGroup[];
  services: Service[];
  cells: AccessMatrixCell[];
}

export const initialLauncherData: LauncherData = {
  clients: mockClients,
  users: mockUsers,
  memberships: mockMemberships,
  groups: mockGroups,
  services: mockServices,
  grants: mockGrants,
  exceptions: mockExceptions,
  invites: mockInvites,
  syncStatuses: mockSyncStatuses,
  auditEvents: mockAuditEvents,
};

export const profileOptions: ProfileOption[] = [
  {
    userId: "user_root",
    label: "DC Touch",
    description: "NODE.DC superadmin",
    defaultClientId: "client_romashka",
  },
  {
    userId: "user_silver_psih",
    label: "Silver Psy",
    description: "DCTOUCH manager",
    defaultClientId: "client_romashka",
  },
];

export function buildMe(data: LauncherData, userId: string, requestedClientId?: string): MeResponse {
  const user = getUser(data, userId);
  const isRoot = user.id === "user_root";
  const availableMemberships = isRoot
    ? data.clients.map((client) => ({
        clientId: client.id,
        clientName: client.name,
        role: "client_owner" as const,
        status: "active" as const,
      }))
    : data.memberships
        .filter((membership) => membership.userId === user.id)
        .map((membership) => ({
          clientId: membership.clientId,
          clientName: getClient(data, membership.clientId).name,
          role: membership.role,
          status: membership.status,
        }));

  const fallbackClientId =
    profileOptions.find((option) => option.userId === user.id)?.defaultClientId ?? availableMemberships[0]?.clientId;
  const canUseRequestedClient = availableMemberships.some((membership) => membership.clientId === requestedClientId);
  const activeClientId = canUseRequestedClient ? requestedClientId! : fallbackClientId;
  const activeMembership = availableMemberships.find((membership) => membership.clientId === activeClientId);
  const launcherRole = resolveLauncherRole({ isRoot, membershipRole: activeMembership?.role });
  const permissions = resolvePermissions({
    launcherRole,
    membershipStatus: activeMembership?.status,
  });

  return {
    user: {
      id: user.id,
      authentikUserId: user.authentikUserId,
      name: user.name,
      email: user.email,
      avatarUrl: user.avatarUrl,
    },
    launcherRole,
    memberships: availableMemberships,
    activeClientId,
    permissions,
    mockAuthentikClaims: {
      sub: user.authentikUserId ?? user.id,
      email: user.email,
      name: user.name,
      groups: buildMockGroups(data, user.id, activeClientId, launcherRole),
      activeClientId,
    },
  };
}

export function buildLauncherServices(data: LauncherData, userId: string, activeClientId: string): LauncherServiceView[] {
  const me = buildMe(data, userId, activeClientId);
  const user = getUser(data, userId);
  const client = getClient(data, activeClientId);
  const membership = getRuntimeMembership(data, user.id, activeClientId, me.launcherRole === "root_admin");
  const userGroups = getUserGroups(data, user.id, activeClientId);
  const isRoot = me.launcherRole === "root_admin";

  return data.services
    .slice()
    .sort((a, b) => a.order - b.order)
    .map((service) => {
      const effectiveAccess = computeEffectiveAccess({
        client,
        user,
        membership,
        userGroups,
        service,
        grants: data.grants,
        exceptions: data.exceptions,
      });

      return {
        id: service.id,
        slug: service.slug,
        title: service.title,
        subtitle: service.subtitle,
        description: service.description,
        fullDescription: service.fullDescription,
        status: service.status,
        userAccess: effectiveAccess.allowed ? ("allowed" as const) : ("denied" as const),
        appRole: effectiveAccess.appRole,
        openUrl: effectiveAccess.openEnabled ? getServiceLaunchLink(service) || null : null,
        accentColor: service.accentColor,
        media: {
          icon: service.iconUrl,
          thumbnail: service.coverImageUrl,
          coverImage: service.coverImageUrl,
          coverKind: service.coverMediaKind,
          coverSource: service.coverMediaSource,
          coverFileName: service.coverMediaFileName,
          previewVideo: service.previewVideoUrl,
          ambientVideo: service.ambientVideoUrl,
          ambientKind: service.ambientMediaKind,
          ambientSource: service.ambientMediaSource,
          ambientFileName: service.ambientMediaFileName,
          fallbackGradient: service.fallbackGradient,
        },
        effectiveAccess,
      };
    })
    .filter((service) => isRoot || service.status !== "hidden");
}

export function buildAccessMatrix(data: LauncherData, clientId: string, includeAllServices: boolean): AccessMatrix {
  const client = getClient(data, clientId);
  const memberships = data.memberships.filter((membership) => membership.clientId === clientId);
  const users = memberships.map((membership) => getUser(data, membership.userId));
  const groups = data.groups.filter((group) => group.clientId === clientId);
  const clientServiceIds = new Set(
    data.grants
      .filter((grant) => grant.targetType === "client" && grant.targetId === clientId && grant.status === "active")
      .map((grant) => grant.serviceId)
  );
  const services = data.services
    .filter((service) => includeAllServices || clientServiceIds.has(service.id) || service.status === "active")
    .sort((a, b) => a.order - b.order);
  const cells = users.flatMap((user) => {
    const membership = memberships.find((item) => item.userId === user.id) ?? getRuntimeMembership(data, user.id, clientId);
    const userGroups = getUserGroups(data, user.id, clientId);

    return services.map((service) => ({
      userId: user.id,
      serviceId: service.id,
      effectiveAccess: computeEffectiveAccess({
        client,
        user,
        membership,
        userGroups,
        service,
        grants: data.grants,
        exceptions: data.exceptions,
      }),
    }));
  });

  return { client, users, groups, services, cells };
}

export function getClient(data: LauncherData, clientId: string): Client {
  const client = data.clients.find((item) => item.id === clientId);
  if (!client) throw new Error(`Unknown client: ${clientId}`);
  return client;
}

export function getUser(data: LauncherData, userId: string): LauncherUser {
  const user = data.users.find((item) => item.id === userId);
  if (!user) throw new Error(`Unknown user: ${userId}`);
  return user;
}

export function getService(data: LauncherData, serviceId: string): Service {
  const service = data.services.find((item) => item.id === serviceId);
  if (!service) throw new Error(`Unknown service: ${serviceId}`);
  return service;
}

export function getClientUsers(data: LauncherData, clientId: string): LauncherUser[] {
  const userIds = new Set(data.memberships.filter((membership) => membership.clientId === clientId).map((item) => item.userId));
  return data.users.filter((user) => userIds.has(user.id));
}

export function getUserGroups(data: LauncherData, userId: string, clientId: string): ClientGroup[] {
  return data.groups.filter((group) => group.clientId === clientId && group.memberIds.includes(userId));
}

export function getRuntimeMembership(
  data: LauncherData,
  userId: string,
  clientId: string,
  allowVirtualRoot = false
): ClientMembership {
  const membership = data.memberships.find((item) => item.userId === userId && item.clientId === clientId);
  if (membership) return membership;

  if (allowVirtualRoot) {
    return {
      id: `virtual_root_${clientId}`,
      clientId,
      userId,
      role: "client_owner",
      status: "active",
      createdAt: "2026-05-01T09:00:00Z",
      updatedAt: "2026-05-01T09:00:00Z",
    };
  }

  return {
    id: `missing_${clientId}_${userId}`,
    clientId,
    userId,
    role: "member",
    status: "disabled",
    createdAt: "2026-05-01T09:00:00Z",
    updatedAt: "2026-05-01T09:00:00Z",
  };
}

function buildMockGroups(
  data: LauncherData,
  userId: string,
  activeClientId: string,
  launcherRole: LauncherGlobalRole
): string[] {
  const groups = getUserGroups(data, userId, activeClientId).map((group) => `client:${activeClientId}:group:${group.name}`);
  return [`launcher:${launcherRole}`, `client:${activeClientId}`, ...groups];
}
