import { EmbedConfigRepository } from '../models/repositories/EmbedConfigRepository';
import { EventTypeRepository } from '../models/repositories/EventTypeRepository';
import { UserRepository } from '../models/repositories/UserRepository';
import { AppError } from '../utils/AppError';
import { generateUuid } from '../utils/uuid';
import { generateToken } from '../utils/crypto';
import { EmbedConfig, EmbedDisplayMode, EmbedTheme } from '../models/entities/EmbedConfig.entity';

export class EmbedService {
  constructor(
    private embedRepo = new EmbedConfigRepository(),
    private eventTypeRepo = new EventTypeRepository(),
    private userRepo = new UserRepository(),
  ) {}

  async getConfig(embedKey: string): Promise<EmbedConfig | null> {
    return this.embedRepo.findByEmbedKey(embedKey);
  }

  isOriginAllowed(origin: string | undefined, allowedOrigins: string[]): boolean {
    if (!origin) return false;
    if (allowedOrigins.includes('*')) return true;
    const normalized = origin.replace(/\/$/, '');
    return allowedOrigins.some((o) => normalized.startsWith(o.replace(/\/$/, '')));
  }

  async getAllowedOrigins(): Promise<Set<string>> {
    const configs = await this.embedRepo.findAllActive();
    const origins = new Set<string>();
    for (const c of configs) {
      for (const o of c.allowedOrigins) origins.add(o);
    }
    return origins;
  }

  async listForUser(userId: string) {
    const configs = await this.embedRepo.findByUserId(userId);
    const user = await this.userRepo.findById(userId);
    return { configs, username: user!.username };
  }

  async create(
    userId: string,
    data: {
      eventTypeId: string;
      displayMode: EmbedDisplayMode;
      allowedOrigins: string[];
      theme?: EmbedTheme;
      hideBranding?: boolean;
    },
  ) {
    const eventType = await this.eventTypeRepo.findById(data.eventTypeId);
    if (!eventType || eventType.userId !== userId) {
      throw new AppError('not_found', 404, 'Event type not found.');
    }
    const user = await this.userRepo.findById(userId);

    const config = await this.embedRepo.create({
      id: generateUuid(),
      userId,
      eventTypeId: data.eventTypeId,
      embedKey: `emb_${generateToken(16)}`,
      displayMode: data.displayMode,
      theme: data.theme ?? {},
      allowedOrigins: data.allowedOrigins,
      hideBranding: data.hideBranding ?? false,
      isActive: true,
    });

    return { config, username: user!.username, eventSlug: eventType.slug };
  }

  async update(userId: string, id: string, data: Partial<EmbedConfig>) {
    const config = await this.embedRepo.findById(id);
    if (!config || config.userId !== userId) {
      throw new AppError('not_found', 404, 'Embed config not found.');
    }
    const updated = await this.embedRepo.update(id, data);
    const user = await this.userRepo.findById(userId);
    const eventType = await this.eventTypeRepo.findById(updated.eventTypeId);
    return { config: updated, username: user!.username, eventSlug: eventType!.slug };
  }

  async deactivate(userId: string, id: string) {
    const config = await this.embedRepo.findById(id);
    if (!config || config.userId !== userId) {
      throw new AppError('not_found', 404, 'Embed config not found.');
    }
    await this.embedRepo.deactivate(id);
  }

  async getOrCreateForEventType(userId: string, eventTypeId: string) {
    const eventType = await this.eventTypeRepo.findById(eventTypeId);
    if (!eventType || eventType.userId !== userId) {
      throw new AppError('not_found', 404, 'Event type not found.');
    }

    let config = await this.embedRepo.findByEventTypeId(userId, eventTypeId);
    if (!config) {
      const created = await this.create(userId, {
        eventTypeId,
        displayMode: 'modal',
        allowedOrigins: ['*'],
        theme: { primaryColor: eventType.color, borderRadius: '8px' },
      });
      return created;
    }

    const user = await this.userRepo.findById(userId);
    return { config, username: user!.username, eventSlug: eventType.slug };
  }

  async updateForEventType(
    userId: string,
    eventTypeId: string,
    data: {
      displayMode?: EmbedDisplayMode;
      allowedOrigins?: string[];
      theme?: EmbedTheme;
      hideBranding?: boolean;
    },
  ) {
    await this.getOrCreateForEventType(userId, eventTypeId);
    const config = await this.embedRepo.findByEventTypeId(userId, eventTypeId);
    if (!config) {
      throw new AppError('not_found', 404, 'Embed config not found.');
    }
    return this.update(userId, config.id, data);
  }
}
