import { Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';

export interface NotificationChannel {
  channelKey: string;
  name: string;
  description: string | null;
  isMandatory: boolean;
  isEnabled: boolean;
  createdAt: Date;
  updatedAt: Date;
}

export interface UserChannelPreference {
  userId: string;
  channelKey: string;
  enabled: boolean;
  updatedAt: Date;
}

@Injectable()
export class ChannelRepository {
  constructor(@InjectDataSource() private readonly ds: DataSource) {}

  async listChannels(): Promise<NotificationChannel[]> {
    const rows = await this.ds.query(
      `SELECT * FROM public.notification_channels WHERE is_enabled = true ORDER BY is_mandatory DESC, channel_key`,
    );
    return rows.map(this.mapChannel);
  }

  async createChannel(dto: {
    channelKey: string;
    name: string;
    description?: string;
    isMandatory?: boolean;
  }): Promise<NotificationChannel> {
    const result = await this.ds.query(
      `INSERT INTO public.notification_channels (channel_key, name, description, is_mandatory)
       VALUES ($1, $2, $3, $4) RETURNING *`,
      [dto.channelKey, dto.name, dto.description ?? null, dto.isMandatory ?? false],
    );
    return this.mapChannel(result[0]);
  }

  async updateChannel(
    channelKey: string,
    dto: { name?: string; description?: string; isMandatory?: boolean; isEnabled?: boolean },
  ): Promise<NotificationChannel | null> {
    const fields: string[] = [];
    const values: any[] = [];
    let idx = 1;
    if (dto.name !== undefined) { fields.push(`name = $${idx++}`); values.push(dto.name); }
    if (dto.description !== undefined) { fields.push(`description = $${idx++}`); values.push(dto.description); }
    if (dto.isMandatory !== undefined) { fields.push(`is_mandatory = $${idx++}`); values.push(dto.isMandatory); }
    if (dto.isEnabled !== undefined) { fields.push(`is_enabled = $${idx++}`); values.push(dto.isEnabled); }
    fields.push(`updated_at = NOW()`);
    values.push(channelKey);
    const result = await this.ds.query(
      `UPDATE public.notification_channels SET ${fields.join(', ')} WHERE channel_key = $${idx} RETURNING *`,
      values,
    );
    return result.length ? this.mapChannel(result[0]) : null;
  }

  async deleteChannel(channelKey: string): Promise<void> {
    await this.ds.query(`DELETE FROM public.notification_channels WHERE channel_key = $1`, [channelKey]);
  }

  // ── User preferences ──────────────────────────────────────────────────────

  async getUserPreferences(userId: string): Promise<UserChannelPreference[]> {
    const rows = await this.ds.query(
      `SELECT p.*, c.name, c.is_mandatory
       FROM public.notification_channels c
       LEFT JOIN public.user_notification_preferences p ON p.user_id = $1 AND p.channel_key = c.channel_key
       WHERE c.is_enabled = true
       ORDER BY c.is_mandatory DESC, c.channel_key`,
      [userId],
    );
    return rows.map((r: any) => ({
      userId,
      channelKey: r.channel_key,
      channelName: r.name,
      isMandatory: r.is_mandatory,
      enabled: r.enabled !== null ? r.enabled : true,
      updatedAt: r.updated_at,
    }));
  }

  async setUserPreference(userId: string, channelKey: string, enabled: boolean): Promise<void> {
    // Cannot disable mandatory channels
    const channel = await this.ds.query(
      `SELECT is_mandatory FROM public.notification_channels WHERE channel_key = $1`,
      [channelKey],
    );
    if (!channel.length) throw new Error(`Channel '${channelKey}' not found`);
    if (channel[0].is_mandatory && !enabled) {
      throw new Error(`Channel '${channelKey}' is mandatory and cannot be disabled`);
    }
    await this.ds.query(
      `INSERT INTO public.user_notification_preferences (user_id, channel_key, enabled)
       VALUES ($1, $2, $3)
       ON CONFLICT (user_id, channel_key) DO UPDATE SET enabled = $3, updated_at = NOW()`,
      [userId, channelKey, enabled],
    );
  }

  async setAllUserPreferences(userId: string, preferences: { channelKey: string; enabled: boolean }[]): Promise<void> {
    for (const pref of preferences) {
      await this.setUserPreference(userId, pref.channelKey, pref.enabled);
    }
  }

  private mapChannel(r: any): NotificationChannel {
    return {
      channelKey: r.channel_key,
      name: r.name,
      description: r.description,
      isMandatory: r.is_mandatory,
      isEnabled: r.is_enabled,
      createdAt: r.created_at,
      updatedAt: r.updated_at,
    };
  }
}
