import type { Client } from "../client/types";
import type { Service } from "../service/types";
import type { ClientGroup, ClientMembership, LauncherUser } from "../user/types";
import type { EffectiveAccessResult, ServiceAccessException, ServiceGrant } from "./types";

export function computeEffectiveAccess(input: {
  client: Client;
  user: LauncherUser;
  membership: ClientMembership;
  userGroups: ClientGroup[];
  service: Service;
  grants: ServiceGrant[];
  exceptions: ServiceAccessException[];
}): EffectiveAccessResult {
  if (input.client.status === "suspended" || input.client.status === "expired") {
    return blocked(input, "Клиент приостановлен или срок доступа истёк");
  }

  if (input.user.globalStatus === "blocked" || input.membership.status === "disabled") {
    return blocked(input, "Пользователь заблокирован или отключён внутри клиента");
  }

  if (input.service.status === "disabled") {
    return blocked(input, "Сервис отключён");
  }

  if (input.service.status === "hidden") {
    return blocked(input, "Сервис скрыт");
  }

  const deny = input.exceptions.find(
    (item) => item.serviceId === input.service.id && item.userId === input.user.id && item.type === "deny"
  );

  if (deny) {
    return {
      serviceId: input.service.id,
      userId: input.user.id,
      allowed: false,
      visible: false,
      openEnabled: false,
      source: "exception",
      sourceId: deny.id,
      reason: "Доступ отключён индивидуальным исключением",
    };
  }

  const allow = input.exceptions.find(
    (item) => item.serviceId === input.service.id && item.userId === input.user.id && item.type === "allow"
  );

  if (allow) {
    return {
      serviceId: input.service.id,
      userId: input.user.id,
      allowed: true,
      visible: true,
      openEnabled: input.service.status === "active",
      source: "exception",
      sourceId: allow.id,
      reason: "Доступ выдан индивидуальным allow-исключением",
    };
  }

  const userGrant = input.grants.find(
    (grant) =>
      grant.serviceId === input.service.id &&
      grant.targetType === "user" &&
      grant.targetId === input.user.id &&
      grant.status === "active"
  );

  if (userGrant) {
    return {
      serviceId: input.service.id,
      userId: input.user.id,
      allowed: true,
      visible: true,
      openEnabled: input.service.status === "active",
      appRole: userGrant.appRole,
      source: "user",
      sourceId: userGrant.id,
      reason: "Доступ выдан пользователю напрямую",
    };
  }

  const groupIds = input.userGroups.map((group) => group.id);
  const groupGrant = input.grants.find(
    (grant) =>
      grant.serviceId === input.service.id &&
      grant.targetType === "group" &&
      groupIds.includes(grant.targetId) &&
      grant.status === "active"
  );

  if (groupGrant) {
    return {
      serviceId: input.service.id,
      userId: input.user.id,
      allowed: true,
      visible: true,
      openEnabled: input.service.status === "active",
      appRole: groupGrant.appRole,
      source: "group",
      sourceId: groupGrant.id,
      reason: "Доступ выдан группе пользователя",
    };
  }

  const clientGrant = input.grants.find(
    (grant) =>
      grant.serviceId === input.service.id &&
      grant.targetType === "client" &&
      grant.targetId === input.client.id &&
      grant.status === "active"
  );

  if (clientGrant) {
    return {
      serviceId: input.service.id,
      userId: input.user.id,
      allowed: true,
      visible: true,
      openEnabled: input.service.status === "active",
      appRole: clientGrant.appRole,
      source: "client",
      sourceId: clientGrant.id,
      reason: "Доступ выдан всему клиенту",
    };
  }

  return blocked(input, "Доступ к сервису не выдан");
}

function blocked(input: {
  service: Service;
  user: LauncherUser;
}, reason: string): EffectiveAccessResult {
  return {
    serviceId: input.service.id,
    userId: input.user.id,
    allowed: false,
    visible: false,
    openEnabled: false,
    reason,
  };
}
