// src/chat/chat.service.ts
import { Injectable, ForbiddenException } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { ChatRoom } from './entities/chat-room.entity';
import { ChatRoomMember } from './entities/chat-room-member.entity';
import { ChatMessage } from './entities/chat-message.entity';
import { CreateRoomDto, SendMessageDto } from './dto/chat.dto';

@Injectable()
export class ChatService {
  constructor(
    @InjectDataSource('scheme_dbs')
    private readonly dataSource: DataSource,
  ) {}

  // ── Get or create direct room between two users ───────────────────────────
  async getOrCreateDirectRoom(userIdA: number, userIdB: number): Promise<ChatRoom> {
    const roomRepo   = this.dataSource.getRepository(ChatRoom);
    const memberRepo = this.dataSource.getRepository(ChatRoomMember);

    // Find existing room with exactly these two members
    const existing = await this.dataSource.query(`
      SELECT r.id FROM chat_rooms r
      INNER JOIN chat_room_members m1 ON m1.room_id = r.id AND m1.user_id = ?
      INNER JOIN chat_room_members m2 ON m2.room_id = r.id AND m2.user_id = ?
      WHERE r.inquiry_id IS NULL
      LIMIT 1
    `, [userIdA, userIdB]) as { id: number }[];

    if (existing.length > 0) {
      // ✅ FIX: use non-null assertion since we just found the record
      const found = await roomRepo.findOne({
        where: { id: existing[0].id },
        relations: ['members', 'members.user'],
      });
      return found!;
    }

    // Create new room
    // ✅ FIX: name is undefined not null — matches entity type
    const room = await roomRepo.save(
      roomRepo.create({ name: undefined, inquiry_id: undefined }),
    );

    await memberRepo.save([
      memberRepo.create({ room_id: room.id, user_id: userIdA }),
      memberRepo.create({ room_id: room.id, user_id: userIdB }),
    ]);

    const created = await roomRepo.findOne({
      where: { id: room.id },
      relations: ['members', 'members.user'],
    });
    return created!; // ✅ FIX: non-null assertion — we just created it
  }

  // ── Create group room ─────────────────────────────────────────────────────
  async createRoom(dto: CreateRoomDto): Promise<ChatRoom> {
    const roomRepo   = this.dataSource.getRepository(ChatRoom);
    const memberRepo = this.dataSource.getRepository(ChatRoomMember);

    const room = await roomRepo.save(
      roomRepo.create({
        name:        dto.name,
        inquiry_id:  dto.inquiry_id,
        inquiry_ref: dto.inquiry_ref,
      }),
    );

    const members = dto.memberIds.map(uid =>
      memberRepo.create({ room_id: room.id, user_id: uid }),
    );
    await memberRepo.save(members);

    const created = await roomRepo.findOne({
      where: { id: room.id },
      relations: ['members', 'members.user'],
    });
    return created!; // ✅ FIX: non-null assertion
  }

  // ── Get all rooms for a user with last message + unread count ─────────────
  async getRoomsForUser(userId: number) {
    const rooms = await this.dataSource.query(`
      SELECT
        r.id,
        r.name,
        r.inquiry_id,
        r.inquiry_ref,
        r.created_at,
        lm.message       AS last_message,
        lm.sender_id     AS last_sender_id,
        lm.created_at    AS last_message_at,
        lm.type          AS last_message_type,
        (
          SELECT COUNT(*) FROM chat_messages cm
          WHERE cm.room_id = r.id
            AND cm.sender_id != ?
            AND cm.created_at > COALESCE(mem.last_read, '1970-01-01')
        ) AS unread_count,
        u.id         AS other_user_id,
        u.firstName  AS other_first_name,
        u.lastName   AS other_last_name,
        u.email      AS other_email
      FROM chat_rooms r
      INNER JOIN chat_room_members mem ON mem.room_id = r.id AND mem.user_id = ?
      LEFT JOIN chat_messages lm ON lm.id = (
        SELECT id FROM chat_messages WHERE room_id = r.id ORDER BY created_at DESC LIMIT 1
      )
      LEFT JOIN chat_room_members other_mem ON other_mem.room_id = r.id AND other_mem.user_id != ?
      LEFT JOIN users u ON u.id = other_mem.user_id
      ORDER BY COALESCE(lm.created_at, r.created_at) DESC
    `, [userId, userId, userId]);

    return rooms;
  }

  // ── Get messages for a room (paginated) ───────────────────────────────────
  async getMessages(roomId: number, userId: number, page = 1, limit = 50) {
    // Verify user is member
    const isMember = await this.dataSource.getRepository(ChatRoomMember).findOne({
      where: { room_id: roomId, user_id: userId },
    });
    if (!isMember) throw new ForbiddenException('Not a member of this room');

    const skip = (page - 1) * limit;
    const messages = await this.dataSource.query(`
      SELECT
        cm.id,
        cm.room_id,
        cm.sender_id,
        cm.message,
        cm.type,
        cm.file_url,
        cm.file_name,
        cm.reply_to_id,
        cm.created_at,
        u.firstName  AS sender_first_name,
        u.lastName   AS sender_last_name,
        u.email      AS sender_email,
        rm.message   AS reply_message,
        ru.firstName AS reply_sender_first
      FROM chat_messages cm
      INNER JOIN users u  ON u.id  = cm.sender_id
      LEFT JOIN  chat_messages rm ON rm.id = cm.reply_to_id
      LEFT JOIN  users ru ON ru.id = rm.sender_id
      WHERE cm.room_id = ?
      ORDER BY cm.created_at DESC
      LIMIT ? OFFSET ?
    `, [roomId, limit, skip]);

    // Return chronologically
    return (messages as any[]).reverse();
  }

  // ── Save a message to DB ──────────────────────────────────────────────────
  async saveMessage(dto: SendMessageDto, senderId: number): Promise<ChatMessage> {
    const msgRepo = this.dataSource.getRepository(ChatMessage);

    const isMember = await this.dataSource.getRepository(ChatRoomMember).findOne({
      where: { room_id: dto.roomId, user_id: senderId },
    });
    if (!isMember) throw new ForbiddenException('Not a member of this room');

    const msg = await msgRepo.save(
      msgRepo.create({
        room_id:     dto.roomId,
        sender_id:   senderId,
        message:     dto.message,
        type:        dto.type ?? 'text',
        file_url:    dto.file_url,
        file_name:   dto.file_name,
        reply_to_id: dto.reply_to_id,
      }),
    );

    const saved = await msgRepo.findOne({
      where: { id: msg.id },
      relations: ['sender'],
    });
    return saved!; // ✅ FIX: non-null assertion — we just created it
  }

  // ── Mark all messages in room as read ─────────────────────────────────────
  async markAsRead(roomId: number, userId: number): Promise<void> {
    await this.dataSource.getRepository(ChatRoomMember).update(
      { room_id: roomId, user_id: userId },
      { last_read: new Date() },
    );
  }

  // ── Total unread across ALL rooms ─────────────────────────────────────────
  async getTotalUnread(userId: number): Promise<number> {
    const result = await this.dataSource.query(`
      SELECT SUM(sub.unread) AS total FROM (
        SELECT
          (SELECT COUNT(*) FROM chat_messages cm
           WHERE cm.room_id = r.id
             AND cm.sender_id != ?
             AND cm.created_at > COALESCE(mem.last_read, '1970-01-01')
          ) AS unread
        FROM chat_rooms r
        INNER JOIN chat_room_members mem ON mem.room_id = r.id AND mem.user_id = ?
      ) sub
    `, [userId, userId]) as { total: string | null }[];

    return Number(result[0]?.total ?? 0);
  }

  // ── Get room members ──────────────────────────────────────────────────────
  async getRoomMembers(roomId: number) {
    return this.dataSource.query(`
      SELECT u.id, u.firstName, u.lastName, u.email
      FROM chat_room_members m
      INNER JOIN users u ON u.id = m.user_id
      WHERE m.room_id = ?
    `, [roomId]);
  }

  // ── Get all users for starting new chat ──────────────────────────────────
  async getAllUsers(currentUserId: number) {
    return this.dataSource.query(`
      SELECT id, firstName, lastName, email
      FROM users
      WHERE id != ?
      ORDER BY firstName ASC
    `, [currentUserId]);
  }

  // ── Get single room ───────────────────────────────────────────────────────
  async getRoom(roomId: number): Promise<ChatRoom | null> {
    return this.dataSource.getRepository(ChatRoom).findOne({
      where: { id: roomId },
      relations: ['members', 'members.user'],
    });
  }
}