import {
  Injectable,
  NotFoundException,
  BadRequestException,
  ConflictException,
  UnauthorizedException,
  Logger,
} from '@nestjs/common';
import { PrismaService } from '@chatapp/database';
import * as crypto from 'crypto';

/**
 * FASE 6 — SCIM Provisioning Service
 *
 * Implementa el protocolo SCIM 2.0 para gestión de usuarios desde
 * sistemas externos (IdP). Soporta operaciones CRUD sobre usuarios
 * y mapeo de atributos.
 */
@Injectable()
export class ScimProvisioningService {
  private readonly logger = new Logger(ScimProvisioningService.name);

  constructor(private readonly prisma: PrismaService) {}

  // ============ SCIM Token management ============

  async createToken(wsId: string, userId: string, name: string) {
    const token = this.generateScimToken();
    const record = await this.prisma.scimToken.create({
      data: {
        workspaceId: wsId,
        token,
        name,
        createdBy: userId,
        isActive: true,
      },
    });
    return {
      id: record.id,
      name: record.name,
      token,
      is_active: record.isActive,
      created_at: record.createdAt,
    };
  }

  async listTokens(wsId: string) {
    const tokens = await this.prisma.scimToken.findMany({
      where: { workspaceId: wsId },
      orderBy: { createdAt: 'desc' },
    });
    return tokens.map((t) => ({
      id: t.id,
      name: t.name,
      is_active: t.isActive,
      last_used_at: t.lastUsedAt,
      created_at: t.createdAt,
    }));
  }

  async revokeToken(wsId: string, tokenId: string) {
    const token = await this.prisma.scimToken.findFirst({
      where: { id: tokenId, workspaceId: wsId },
    });
    if (!token) throw new NotFoundException('Token not found');
    return this.prisma.scimToken.update({
      where: { id: tokenId },
      data: { isActive: false },
    });
  }

  /**
   * Valida un token SCIM y devuelve el workspace.
   */
  async validateToken(token: string): Promise<string> {
    const record = await this.prisma.scimToken.findUnique({
      where: { token },
    });
    if (!record || !record.isActive) {
      throw new UnauthorizedException('Invalid SCIM token');
    }
    // Actualizar lastUsedAt
    await this.prisma.scimToken.update({
      where: { id: record.id },
      data: { lastUsedAt: new Date() },
    });
    return record.workspaceId;
  }

  // ============ SCIM User operations ============

  /**
   * GET /Users — Lista usuarios del workspace en formato SCIM.
   */
  async listUsers(wsId: string, opts: { count?: number; startIndex?: number; filter?: string } = {}) {
    const count = Math.min(opts.count ?? 100, 200);
    const startIndex = Math.max(opts.startIndex ?? 1, 1);
    const skip = startIndex - 1;

    let where: any = { workspaceId: wsId, deactivatedAt: null };
    // Parsear filtro SCIM simple (userName eq "email@domain")
    if (opts.filter) {
      const filterMatch = opts.filter.match(/userName\s+eq\s+"([^"]+)"/);
      if (filterMatch) {
        const user = await this.prisma.user.findUnique({ where: { email: filterMatch[1] } });
        if (user) {
          const member = await this.prisma.workspaceMember.findUnique({
            where: { workspaceId_userId: { workspaceId: wsId, userId: user.id } },
          });
          if (member) {
            return {
              schemas: ['urn:ietf:params:scim:api:messages:2.0:ListResponse'],
              totalResults: 1,
              startIndex,
              itemsPerPage: 1,
              Resources: [this.userToScim(user, member)],
            };
          }
        }
        return {
          schemas: ['urn:ietf:params:scim:api:messages:2.0:ListResponse'],
          totalResults: 0,
          startIndex,
          itemsPerPage: 0,
          Resources: [],
        };
      }
    }

    const [members, total] = await Promise.all([
      this.prisma.workspaceMember.findMany({
        where,
        skip,
        take: count,
        orderBy: { joinedAt: 'asc' },
        include: { user: true },
      }),
      this.prisma.workspaceMember.count({ where }),
    ]);

    return {
      schemas: ['urn:ietf:params:scim:api:messages:2.0:ListResponse'],
      totalResults: total,
      startIndex,
      itemsPerPage: count,
      Resources: members.map((m) => this.userToScim(m.user, m)),
    };
  }

  /**
   * GET /Users/:id — Obtiene un usuario por ID.
   */
  async getUser(wsId: string, userId: string) {
    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId } },
      include: { user: true },
    });
    if (!member) throw new NotFoundException('User not found');

    return this.userToScim(member.user, member);
  }

  /**
   * POST /Users — Crea un nuevo usuario via SCIM.
   */
  async createUser(wsId: string, scimUser: any) {
    const email = scimUser.userName || scimUser.emails?.[0]?.value;
    if (!email) throw new BadRequestException('Email is required');

    // Verificar si ya existe
    const existing = await this.prisma.user.findUnique({ where: { email } });
    if (existing) {
      // Verificar si ya es miembro
      const existingMember = await this.prisma.workspaceMember.findUnique({
        where: { workspaceId_userId: { workspaceId: wsId, userId: existing.id } },
      });
      if (existingMember) {
        throw new ConflictException('User already exists');
      }
    }

    const displayName = scimUser.displayName || email.split('@')[0];
    const givenName = scimUser.name?.givenName;
    const familyName = scimUser.name?.familyName;
    const realName = givenName && familyName ? `${givenName} ${familyName}` : displayName;

    let user;
    if (existing) {
      user = existing;
    } else {
      user = await this.prisma.user.create({
        data: {
          email,
          displayName,
          realName,
          emailVerified: true,
        },
      });
    }

    const role = this.mapScimRole(scimUser);

    const member = await this.prisma.workspaceMember.create({
      data: {
        workspaceId: wsId,
        userId: user.id,
        role,
      },
    });

    // Log de auditoría
    await this.prisma.auditLog.create({
      data: {
        workspaceId: wsId,
        actorType: 'system',
        action: 'scim.user.created',
        entityType: 'user',
        entityId: user.id,
        details: { email, scim_provisioned: true },
      },
    });

    return this.userToScim(user, member);
  }

  /**
   * PUT /Users/:id — Actualiza un usuario completo.
   */
  async updateUser(wsId: string, userId: string, scimUser: any) {
    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId } },
      include: { user: true },
    });
    if (!member) throw new NotFoundException('User not found');

    const data: any = {};
    if (scimUser.displayName) data.displayName = scimUser.displayName;
    if (scimUser.name?.givenName || scimUser.name?.familyName) {
      data.realName = [scimUser.name?.givenName, scimUser.name?.familyName].filter(Boolean).join(' ');
    }

    const user = await this.prisma.user.update({ where: { id: userId }, data });

    // Actualizar rol si viene
    if (scimUser.role || scimUser.roles?.[0]?.value) {
      const role = this.mapScimRole(scimUser);
      await this.prisma.workspaceMember.update({
        where: { workspaceId_userId: { workspaceId: wsId, userId } },
        data: { role },
      });
    }

    return this.userToScim(user, member);
  }

  /**
   * PATCH /Users/:id — Actualización parcial.
   */
  async patchUser(wsId: string, userId: string, operations: any[]) {
    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId } },
      include: { user: true },
    });
    if (!member) throw new NotFoundException('User not found');

    const userData: any = {};
    const memberData: any = {};

    for (const op of operations) {
      if (op.op === 'replace') {
        const path = op.path;
        if (path === 'displayName' && op.value) {
          userData.displayName = op.value;
        } else if (path === 'active') {
          if (op.value === false) {
            memberData.deactivatedAt = new Date();
          } else {
            memberData.deactivatedAt = null;
          }
        } else if (path === 'role' && op.value) {
          memberData.role = this.normalizeRole(op.value);
        }
      }
    }

    let user = member.user;
    if (Object.keys(userData).length > 0) {
      user = await this.prisma.user.update({ where: { id: userId }, data: userData });
    }
    if (Object.keys(memberData).length > 0) {
      await this.prisma.workspaceMember.update({
        where: { workspaceId_userId: { workspaceId: wsId, userId } },
        data: memberData,
      });
    }

    return this.userToScim(user, { ...member, ...memberData });
  }

  /**
   * DELETE /Users/:id — Elimina/desactiva un usuario.
   */
  async deleteUser(wsId: string, userId: string) {
    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId } },
    });
    if (!member) throw new NotFoundException('User not found');

    // Desactivar en lugar de eliminar
    await this.prisma.workspaceMember.update({
      where: { workspaceId_userId: { workspaceId: wsId, userId } },
      data: { deactivatedAt: new Date() },
    });

    // Log de auditoría
    await this.prisma.auditLog.create({
      data: {
        workspaceId: wsId,
        actorType: 'system',
        action: 'scim.user.deactivated',
        entityType: 'user',
        entityId: userId,
        details: { scim_deprovisioned: true },
      },
    });

    return { deleted: true };
  }

  // ============ Group operations (basic) ============

  async listGroups(wsId: string) {
    const groups = await this.prisma.userGroup.findMany({
      where: { workspaceId: wsId },
      include: { _count: { select: { members: true } } },
    });

    return {
      schemas: ['urn:ietf:params:scim:api:messages:2.0:ListResponse'],
      totalResults: groups.length,
      Resources: groups.map((g) => ({
        id: g.id,
        displayName: g.name,
        members: [],
      })),
    };
  }

  // ============ Helpers ============

  private userToScim(user: any, member: any): any {
    return {
      schemas: ['urn:ietf:params:scim:schemas:core:2.0:User'],
      id: user.id,
      userName: user.email,
      displayName: user.displayName,
      name: {
        givenName: user.realName?.split(' ')[0] || '',
        familyName: user.realName?.split(' ').slice(1).join(' ') || '',
      },
      emails: [{ value: user.email, primary: true }],
      active: member.deactivatedAt === null,
      roles: [{ value: member.role, primary: true }],
      meta: {
        resourceType: 'User',
        created: user.createdAt,
        lastModified: user.updatedAt,
      },
    };
  }

  private mapScimRole(scimUser: any): string {
    const role = scimUser.role || scimUser.roles?.[0]?.value || 'member';
    return this.normalizeRole(role);
  }

  private normalizeRole(role: string): string {
    const lower = role.toLowerCase();
    if (['owner', 'admin', 'member', 'guest'].includes(lower)) return lower;
    return 'member';
  }

  private generateScimToken(): string {
    return `scim_${crypto.randomBytes(32).toString('hex')}`;
  }
}