import { Injectable } from '@nestjs/common';
import { DatabaseService } from '../../config';
import {
  ALL_PERMISSION_KEYS,
  getDefaultPermissionMap,
  PermissionValue,
} from '../../modules/access-control/permission-catalog';
import { PermissionUserContext } from './permission-context.types';

@Injectable()
export class PermissionResolverService {
  constructor(private readonly db: DatabaseService) {}

  isOwnerBypass(ctx: PermissionUserContext): boolean {
    return ctx.role === 'owner';
  }

  private async getRolePermissionsMap(roleId: string) {
    const rows = await this.db.role_permissions.findMany({
      where: { role_id: roleId },
    });

    return rows.reduce(
      (
        acc: Record<string, string>,
        row: { permission_key: string; permission_value: string },
      ) => {
        acc[row.permission_key] = row.permission_value;
        return acc;
      },
      {},
    );
  }

  private mergePermissionMap(
    slug: string,
    overrides: Record<string, string> = {},
  ): Record<string, PermissionValue> {
    const defaults = getDefaultPermissionMap(slug);
    const merged = { ...defaults };

    for (const [key, value] of Object.entries(overrides)) {
      if (ALL_PERMISSION_KEYS.includes(key)) {
        merged[key] = value as PermissionValue;
      }
    }

    return merged;
  }

  async resolveAccessRoleId(ctx: PermissionUserContext): Promise<string | null> {
    if (ctx.role === 'owner') {
      const role = await this.db.access_roles.findFirst({
        where: { owner_id: ctx.owner_id, slug: 'owner' },
        select: { id: true },
      });
      return role?.id ?? null;
    }

    if (ctx.sub) {
      const member = await this.db.members.findFirst({
        where: { id: ctx.sub, owner_id: ctx.owner_id },
        select: { access_role_id: true, status: true },
      });

      if (!member || member.status !== 'active') {
        return null;
      }

      return member.access_role_id ?? null;
    }

    if (ctx.access_role_id) {
      return ctx.access_role_id;
    }

    const role = await this.db.access_roles.findFirst({
      where: { owner_id: ctx.owner_id, slug: ctx.role },
      select: { id: true },
    });

    return role?.id ?? null;
  }

  async resolveForUser(
    ctx: PermissionUserContext,
  ): Promise<Record<string, PermissionValue>> {
    if (this.isOwnerBypass(ctx)) {
      return getDefaultPermissionMap('owner');
    }

    const accessRoleId = await this.resolveAccessRoleId(ctx);

    if (!accessRoleId) {
      const fallbackSlug =
        ctx.role === 'admin'
          ? 'admin'
          : ctx.role === 'owner'
            ? 'owner'
            : 'member';
      return getDefaultPermissionMap(fallbackSlug);
    }

    const accessRole = await this.db.access_roles.findFirst({
      where: { id: accessRoleId, owner_id: ctx.owner_id },
      select: { id: true, slug: true },
    });

    if (!accessRole) {
      return getDefaultPermissionMap('member');
    }

    const stored = await this.getRolePermissionsMap(accessRole.id);
    return this.mergePermissionMap(accessRole.slug, stored);
  }
}
