import { createHmac } from 'crypto';
import { Prisma } from '@prisma/client';
import { prisma } from '../../config/database';
import { env } from '../../config/env';
import { logger } from '../../utils/logger';
import { rocketchatClient } from '../rocketchat.client';
import type { ServiceProvisioner, ProvisionerConfig, ProvisionResult, CMUser } from './provisioner.interface';

const ROLE_MAP: Record<string, string[]> = {
  SUPER_ADMIN: ['admin'],
  INFLUENCE_ADMIN: ['moderator'],
  MAP_ADMIN: ['moderator'],
  USER: ['user'],
  TEMP: ['user'],
};

/** Deterministic password — never exposed to users, only used for RC internal auth */
function generateRCPassword(userId: string): string {
  const salt = env.SERVICE_PASSWORD_SALT;
  return createHmac('sha256', salt)
    .update(`rc:${userId}`)
    .digest('hex');
}

/** Safe username from email with collision avoidance suffix */
function generateUsername(email: string, suffix = 0): string {
  const base = email.split('@')[0].toLowerCase().replace(/[^a-z0-9._-]/g, '');
  return suffix > 0 ? `${base}${suffix}` : base;
}

class RocketChatProvisioner implements ServiceProvisioner {
  readonly config: ProvisionerConfig = {
    serviceKey: 'rocketchat',
    displayName: 'Rocket.Chat',
    featureFlag: 'enableChat',
    permissionsKey: '_rcUserId',
    roleMap: ROLE_MAP,
    excludeRoles: [], // TEMP users get 'user' role, still provisioned for chat
  };

  async isAvailable(): Promise<boolean> {
    return rocketchatClient.healthCheck();
  }

  async provision(user: CMUser): Promise<ProvisionResult> {
    try {
      // Check if already provisioned
      let rcUser = await rocketchatClient.findUserByEmail(user.email);

      if (!rcUser) {
        // Create with collision-safe username
        let username = generateUsername(user.email);
        let suffix = 0;
        const maxAttempts = 5;

        while (suffix < maxAttempts) {
          try {
            rcUser = await rocketchatClient.createUser({
              email: user.email,
              name: user.name || user.email.split('@')[0],
              username,
              password: generateRCPassword(user.id),
              roles: ROLE_MAP[user.role] || ['user'],
            });
            break;
          } catch (err) {
            if (err instanceof Error && err.message.includes('already in use')) {
              suffix++;
              username = generateUsername(user.email, suffix);
            } else {
              throw err;
            }
          }
        }

        if (!rcUser) {
          return { success: false, error: 'Failed to create RC user after retries' };
        }

        logger.info(`RC provisioner: created user ${rcUser._id} for ${user.email}`);
      }

      // Persist RC user ID in permissions
      const permissions = (user.permissions as Record<string, unknown>) || {};
      await prisma.user.update({
        where: { id: user.id },
        data: {
          permissions: {
            ...permissions,
            [this.config.permissionsKey]: rcUser._id,
          } as unknown as Prisma.InputJsonValue,
        },
      });

      return { success: true, serviceUserId: rcUser._id };
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      logger.error(`RC provisioner: provision failed for ${user.email}: ${msg}`);
      return { success: false, error: msg };
    }
  }

  async syncUser(user: CMUser, serviceUserId: string): Promise<void> {
    const rcRoles = ROLE_MAP[user.role] || ['user'];
    await rocketchatClient.updateUser(serviceUserId, {
      name: user.name || user.email.split('@')[0],
      roles: rcRoles,
    });
  }

  async deactivate(serviceUserId: string): Promise<void> {
    await rocketchatClient.setUserActive(serviceUserId, false);
  }

  async getAuthToken(user: CMUser, serviceUserId: string): Promise<string | null> {
    try {
      // Sync roles on every auth token request
      await this.syncUser(user, serviceUserId).catch(err => {
        logger.warn('RC role sync failed during auth, continuing:', err);
      });

      const tokenData = await rocketchatClient.createUserToken(serviceUserId);
      return tokenData.authToken;
    } catch (err) {
      logger.error('RC provisioner: getAuthToken failed:', err);
      return null;
    }
  }
}

export const rocketchatProvisioner = new RocketChatProvisioner();
