import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { AuthenticatedUser } from '../auth/auth.types';
import { normalizeText } from '../common/normalize';
import { PrismaService } from '../prisma/prisma.service';
import { CreateRoleDto } from './dto/create-role.dto';
import { UpdateRoleDto } from './dto/update-role.dto';

type PermissionRow = {
  id: string;
  label: string;
  icon: string;
  description: string;
  values: Record<string, boolean | null>;
};

const defaultRoles: Array<{
  key: string;
  name: string;
  summary: string;
  badge: string;
  tone: 'primary' | 'secondary' | 'tertiary';
  icon: string;
  permissions: PermissionRow[];
}> = [
  {
    key: 'admin',
    name: 'Admin',
    summary: 'Full access to all modules, security controls, system configuration, and account-wide actions.',
    badge: 'Active',
    tone: 'primary',
    icon: 'shield_person',
    permissions: [
      { id: 'campaigns', label: 'Manage Campaigns', icon: 'campaign', description: 'Broadcasts and outbound campaign controls.', values: { view: true, edit: true, delete: true, manage: true } },
      { id: 'analytics', label: 'View Analytics', icon: 'monitoring', description: 'KPI, trends, and performance dashboards.', values: { view: true, edit: null, delete: null, manage: true } },
      { id: 'settings', label: 'Edit Settings', icon: 'settings', description: 'Providers, secrets, and environment-facing settings.', values: { view: true, edit: true, delete: true, manage: true } },
      { id: 'billing', label: 'Billing & Invoices', icon: 'payments', description: 'Plan usage, invoices, and billing visibility.', values: { view: true, edit: null, delete: null, manage: true } },
    ],
  },
  {
    key: 'editor',
    name: 'Editor',
    summary: 'Can create templates and manage campaigns, but cannot change security policy or critical settings.',
    badge: 'Standard',
    tone: 'secondary',
    icon: 'edit_document',
    permissions: [
      { id: 'campaigns', label: 'Manage Campaigns', icon: 'campaign', description: 'Broadcasts and outbound campaign controls.', values: { view: true, edit: true, delete: false, manage: false } },
      { id: 'analytics', label: 'View Analytics', icon: 'monitoring', description: 'KPI, trends, and performance dashboards.', values: { view: true, edit: null, delete: null, manage: false } },
      { id: 'settings', label: 'Edit Settings', icon: 'settings', description: 'Providers, secrets, and environment-facing settings.', values: { view: false, edit: false, delete: false, manage: false } },
      { id: 'billing', label: 'Billing & Invoices', icon: 'payments', description: 'Plan usage, invoices, and billing visibility.', values: { view: false, edit: null, delete: null, manage: false } },
    ],
  },
  {
    key: 'agent',
    name: 'Agent',
    summary: 'Focused access for daily operations: conversations, contact handling, and high-signal analytics visibility.',
    badge: 'Standard',
    tone: 'tertiary',
    icon: 'support_agent',
    permissions: [
      { id: 'campaigns', label: 'Manage Campaigns', icon: 'campaign', description: 'Broadcasts and outbound campaign controls.', values: { view: true, edit: false, delete: false, manage: false } },
      { id: 'analytics', label: 'View Analytics', icon: 'monitoring', description: 'KPI, trends, and performance dashboards.', values: { view: true, edit: null, delete: null, manage: false } },
      { id: 'settings', label: 'Edit Settings', icon: 'settings', description: 'Providers, secrets, and environment-facing settings.', values: { view: false, edit: false, delete: false, manage: false } },
      { id: 'billing', label: 'Billing & Invoices', icon: 'payments', description: 'Plan usage, invoices, and billing visibility.', values: { view: false, edit: null, delete: null, manage: false } },
    ],
  },
];

@Injectable()
export class RolesService {
  constructor(private readonly prisma: PrismaService) {}

  async findAll() {
    await this.ensureDefaultRoles();

    const roles = await this.prisma.role.findMany({
      include: {
        _count: {
          select: { users: true },
        },
      },
      orderBy: { createdAt: 'asc' },
    });

    return roles.map((role) => ({
      id: role.id,
      key: role.key,
      name: role.name,
      summary: role.summary,
      badge: role.badge,
      tone: role.tone,
      icon: role.icon,
      usersAssigned: role._count.users,
      permissions: role.permissionsJson,
    }));
  }

  async create(dto: CreateRoleDto, user: AuthenticatedUser, ipAddress?: string) {
    await this.ensureDefaultRoles();
    const actor = await this.findActor(user.sub, user.email);
    const name = normalizeText(dto.name)!;
    const key = this.slugify(name);

    const role = await this.prisma.role.create({
      data: {
        key,
        name,
        summary: normalizeText(dto.summary) || 'Custom role for a focused operational access policy.',
        badge: normalizeText(dto.badge) || 'Custom',
        tone: dto.tone || 'secondary',
        icon: normalizeText(dto.icon) || 'verified_user',
        permissionsJson: dto.permissions as Prisma.InputJsonValue,
      },
      include: {
        _count: { select: { users: true } },
      },
    });

    await this.prisma.auditLog.create({
      data: {
        actorUserId: actor.id,
        actorName: actor.name,
        actorEmail: actor.email,
        actionType: 'Role Created',
        module: 'Access Control',
        ipAddress: ipAddress || null,
        severity: 'default',
        details: `Created role ${role.name}.`,
      },
    });

    return {
      id: role.id,
      key: role.key,
      name: role.name,
      summary: role.summary,
      badge: role.badge,
      tone: role.tone,
      icon: role.icon,
      usersAssigned: role._count.users,
      permissions: role.permissionsJson,
    };
  }

  async update(id: string, dto: UpdateRoleDto, user: AuthenticatedUser, ipAddress?: string) {
    const actor = await this.findActor(user.sub, user.email);

    const role = await this.prisma.role.update({
      where: { id },
      data: {
        ...(dto.name !== undefined ? { name: normalizeText(dto.name)! } : {}),
        ...(dto.summary !== undefined ? { summary: normalizeText(dto.summary) || '' } : {}),
        ...(dto.badge !== undefined ? { badge: normalizeText(dto.badge) || 'Custom' } : {}),
        ...(dto.tone !== undefined ? { tone: dto.tone } : {}),
        ...(dto.icon !== undefined ? { icon: normalizeText(dto.icon) || 'verified_user' } : {}),
        ...(dto.permissions !== undefined
          ? { permissionsJson: dto.permissions as Prisma.InputJsonValue }
          : {}),
      },
      include: {
        _count: { select: { users: true } },
      },
    });

    await this.prisma.auditLog.create({
      data: {
        actorUserId: actor.id,
        actorName: actor.name,
        actorEmail: actor.email,
        actionType: 'User Role Updated',
        module: 'Access Control',
        ipAddress: ipAddress || null,
        severity: 'default',
        details: `Updated role ${role.name}.`,
      },
    });

    return {
      id: role.id,
      key: role.key,
      name: role.name,
      summary: role.summary,
      badge: role.badge,
      tone: role.tone,
      icon: role.icon,
      usersAssigned: role._count.users,
      permissions: role.permissionsJson,
    };
  }

  private async ensureDefaultRoles() {
    const count = await this.prisma.role.count();
    if (count > 0) {
      return;
    }

    for (const role of defaultRoles) {
      await this.prisma.role.create({
        data: {
          key: role.key,
          name: role.name,
          summary: role.summary,
          badge: role.badge,
          tone: role.tone,
          icon: role.icon,
          permissionsJson: role.permissions as Prisma.InputJsonValue,
        },
      });
    }

    const adminRole = await this.prisma.role.findUnique({ where: { key: 'admin' } });
    if (adminRole) {
      await this.prisma.user.updateMany({
        where: { roleId: null },
        data: { roleId: adminRole.id },
      });
    }
  }

  private async findActor(userId: string, email: string) {
    const actor = await this.prisma.user.findUnique({
      where: { id: userId },
      select: { id: true, name: true, email: true },
    });

    return {
      id: actor?.id || userId,
      name: actor?.name || email,
      email: actor?.email || email,
    };
  }

  private slugify(value: string) {
    return (
      value
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/(^-|-$)/g, '') || `role-${Date.now()}`
    );
  }
}
