import { Conversation } from '@/entities/conversation.entity';
import { Message } from '@/entities/message.entity';
import { formatTextIfValid } from '@/features/conver-message';
import { formatTimeAU } from '@/features/format-time-au';
import { isBase64 } from '@/features/is-base64';
import AppResponse from '@/system/filters/response/app-response';
import { SystemLang } from '@/system/lang/system.lang';
import {
  BadRequestException,
  HttpStatus,
  Injectable,
  Logger,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { isJSON } from 'class-validator';
import { paginate, PaginateQuery } from 'nestjs-paginate';
import { Repository } from 'typeorm';
import { CreateBulkMessageDto } from './dtos/create-bulk-message.dto';
import { CreateMessageDto } from './dtos/create-message.dto';
import { ReplyMessageDto } from './dtos/reply-message.dto';
import { SendMessageDto } from './dtos/send-message.dto';
import { MessagesEventService } from './messages-event.service';
import { ZulipService } from './zulip.service';
@Injectable()
export class MessagesService {
  private readonly logger = new Logger(MessagesService.name);

  constructor(
    @InjectRepository(Message)
    readonly repo: Repository<Message>,
    @InjectRepository(Conversation)
    readonly conversationRepo: Repository<Conversation>,
    private readonly event: MessagesEventService,
    private readonly zulipService: ZulipService,
  ) {}

  async index(query: PaginateQuery) {
    const result = await paginate(query, this.repo, {
      sortableColumns: ['created_at'],
      searchableColumns: ['message'],
      defaultLimit: 10,
      filterableColumns: {
        id: true,
        time: true,
        message: true,
        updated_at: true,
        created_at: true,
        'conversation.id': true,
      },
      maxLimit: 100,
      defaultSortBy: [['time_raw', 'DESC']],
    });

    return AppResponse.toPagination<Message>(result, true, Message);
  }

  //   async create(
  //     dto: CreateMessageDto,
  //   ): Promise<{ data: Message; exit: boolean }> {
  //     const time = new Date(dto.time);

  //     const existing = await this.repo.findOne({
  //       where: { time_raw: dto.time, room_id: dto.room_id },
  //     });

  //     if (existing) {
  //       return { data: existing, exit: true };
  //     }

  //     const conversation = await this.conversationRepo.findOne({
  //       where: { id: dto.room_id },
  //     });

  //     if (!conversation)
  //       throw new BadRequestException(
  //         AppResponse.toResponse(null, {
  //           message: SystemLang.getText('messages', 'not_found'),
  //         }),
  //       );

  //     const entity = this.repo.create({
  //       ...dto,
  //       time,
  //       time_raw: dto.time,
  //     });

  //     const result = await this.repo.save(entity);

  //     if (result) {
  //       if (
  //         !conversation.name
  //           .toLocaleLowerCase()
  //           .includes(process.env.GROUP_PREFIX.toLocaleLowerCase()) ||
  //         conversation.type !== 'group'
  //       )
  //         return;

  //       if (!conversation) return;

  //       // Handle when message is a resource
  //       if (isBase64(result.message)) {
  //         const fileUrl = await this.zulipService.uploadFileToZulip(
  //           result.message,
  //         );
  //         const content = `[](${fileUrl})`;

  //         await this.zulipService.sendMessageToTopic(
  //           process.env.ZULIP_STREAMS_NAME,
  //           conversation.name,
  //           content,
  //         );

  //         await this.repo.update(result.id, { message: fileUrl });
  //       } else {
  //         const content = `** :rocket: ${result.name} sent - ${formatTimeAU(result.time_raw)}:**
  // \`\`\`
  // ${formatTextIfValid(result.message)}
  // \`\`\``;

  //         await this.zulipService.sendMessageToTopic(
  //           process.env.ZULIP_STREAMS_NAME,
  //           conversation.name,
  //           content,
  //         );
  //       }
  //     }

  //     return { data: result, exit: false };
  //   }

  encodeSharingUrl(url: string) {
    let encoded = btoa(url); // Base64 thường
    encoded = encoded
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');
    return 'u!' + encoded;
  }

  toDownloadLink(encoded: string) {
    return `https://graph.microsoft.com/v1.0/shares/${encoded}/driveItem?select=restricted,webDavUrl,@microsoft.graph.downloadUrl,file,name`;
  }

  async create(
    dto: CreateMessageDto,
  ): Promise<{ data: Message; exit: boolean }> {
    const time = new Date(dto.time);

    // 1. Kiểm tra message đã tồn tại chưa
    const existing = await this.repo.findOne({
      where: { time_raw: dto.time, room_id: dto.room_id },
    });

    if (existing) {
      return { data: existing, exit: true };
    }

    // 2. Lấy thông tin conversation
    const conversation = await this.conversationRepo.findOne({
      where: { id: dto.room_id },
    });
    if (!conversation) {
      throw new BadRequestException(
        AppResponse.toResponse(null, {
          message: SystemLang.getText('messages', 'not_found'),
        }),
      );
    }

    // 3. Chuẩn hóa message trước khi lưu
    let messages: string[] = Array.isArray(dto.message)
      ? dto.message
      : [dto.message];

    const finalMessages: string[] = [];

    // for (const msg of messages) {
    //   if (isBase64(msg)) {
    //     // Nếu là base64 → upload lên Zulip trước
    //     const fileUrl = await this.zulipService.uploadBase64ToZulip(msg);
    //     finalMessages.push(fileUrl); // Lưu link ảnh thay vì base64
    //   } else if (isJSON(msg)) {
    //     console.log({ msg });

    //     const data = JSON.parse(msg);

    //     if (!data?.url || !data?.token) {
    //       finalMessages.push(msg); // Lưu nguyên text
    //       continue;
    //     }

    //     const encoded = this.encodeSharingUrl(data?.url);

    //     const downloadUrl = this.toDownloadLink(encoded);

    //     try {
    //       const fileUrl = await this.zulipService.uploadGraphLinkToZulip({
    //         url: downloadUrl,
    //         token: data.token,
    //       });

    //       console.log({ fileUrl });

    //       const newData = { ...data, url: fileUrl, origin_url: data.url };

    //       finalMessages.push(JSON.stringify(newData)); // Lưu nguyên text
    //     } catch (error) {
    //       finalMessages.push(msg); // Lưu nguyên text
    //       continue;
    //     }

    //     finalMessages.push(msg); // Lưu nguyên text
    //   } else {
    //     finalMessages.push(msg); // Lưu nguyên text
    //   }
    // }

    for (const msg of messages) {
      if (isBase64(msg)) {
        // Nếu là base64 → upload lên Zulip trước
        const fileUrl = await this.zulipService.uploadBase64ToZulip(msg);
        finalMessages.push(fileUrl); // Lưu link ảnh thay vì base64
        continue;
      }

      if (isJSON(msg)) {
        const data = JSON.parse(msg);

        if (!data?.url || !data?.token) {
          finalMessages.push(msg); // Lưu nguyên text nếu thiếu dữ liệu
          continue;
        }

        const encoded = this.encodeSharingUrl(data.url);
        const downloadUrl = this.toDownloadLink(encoded);

        try {
          const fileUrl = await this.zulipService.uploadGraphLinkToZulip({
            url: downloadUrl,
            token: data.token,
          });

          const newData = { ...data, url: fileUrl, origin_url: data.url };
          finalMessages.push(JSON.stringify(newData)); // ✅ chỉ push data đã cập nhật
        } catch (error) {
          finalMessages.push(msg); // push message gốc khi lỗi
        }

        continue; // kết thúc vòng lặp để tránh chạy xuống dưới
      }

      // Trường hợp còn lại → push nguyên text
      finalMessages.push(msg);
    }

    // 4. Lưu vào DB với message đã xử lý
    const entity = this.repo.create({
      ...dto,
      message: JSON.stringify(finalMessages),
      time,
      time_raw: dto.time,
    });

    const result = await this.repo.save(entity);
    if (!result) return { data: null, exit: false };

    // 5. Kiểm tra conversation có hợp lệ để gửi Zulip không
    const groupPrefix = process.env.GROUP_PREFIX?.toLocaleLowerCase() || '';
    if (
      !conversation?.name?.toLocaleLowerCase().includes(groupPrefix) ||
      conversation?.type !== 'group'
    ) {
      return { data: result, exit: false };
    }

    const buildZulipMessageContent = (
      msgs: string[],
      result: Message,
    ): string => {
      const imageUris: string[] = [];
      const textMessages: string[] = [];
      const fileLinks: string[] = []; // Chứa danh sách file dạng [name](url)

      for (const msg of msgs) {
        // 1. Nếu là JSON và có type === 'file'
        try {
          const parsed = JSON.parse(msg) || msg;
          if (parsed?.type === 'file' && parsed.url && parsed.name) {
            fileLinks.push(`[${parsed.name}](${parsed.url})`);
            continue;
          }
        } catch (error) {
          // Không phải JSON → bỏ qua, xử lý như text
        }

        // 2. Nếu là link ảnh upload → hiển thị dạng image
        if (/\/user_uploads\//.test(msg)) {
          imageUris.push(`[image](${msg.replace(/^\/api\/v1/, '')})`);
          continue;
        }

        // 3. Còn lại là text thường
        textMessages.push(formatTextIfValid(msg));
      }

      // ==== Build nội dung cuối ====
      let finalContent = `** :rocket: ${result.name} sent - ${formatTimeAU(result.time_raw)}:**\n`;

      // Text chính → code block
      if (textMessages.length > 0) {
        finalContent += `\`\`\`\n${textMessages.join('\n')}\n\`\`\`\n`;
      }

      // Ảnh → dưới text
      if (imageUris.length > 0) {
        finalContent += `${imageUris.join('\n')}\n`;
      }

      // File → phần cuối cùng
      if (fileLinks.length > 0) {
        finalContent += `\n**Files:**\n${fileLinks.join('\n')}`;
      }

      return finalContent.trim();
    };

    const finalMessage = buildZulipMessageContent(finalMessages, result);

    // 7. Gửi lên Zulip
    await this.zulipService.sendMessageToTopic(
      process.env.ZULIP_STREAMS_NAME,
      conversation.name,
      finalMessage,
    );

    return { data: result, exit: false };
  }

  async bulkCreate(dtos: CreateMessageDto[]): Promise<Message[]> {
    const conditions = dtos.map((dto) => ({
      time_raw: dto.time,
      room_id: dto.room_id ?? null,
    }));

    // Dùng QueryBuilder để tìm các message đã tồn tại
    const qb = this.repo.createQueryBuilder('message');
    conditions.forEach((cond, i) => {
      const timeParam = `time_${i}`;
      const roomParam = `room_${i}`;
      const clause = cond.room_id
        ? `(message.time_raw = :${timeParam} AND message.room_id = :${roomParam})`
        : `(message.time_raw = :${timeParam} AND message.room_id IS NULL)`;
      if (i === 0) {
        qb.where(clause);
      } else {
        qb.orWhere(clause);
      }
      qb.setParameter(timeParam, cond.time_raw);
      if (cond.room_id) qb.setParameter(roomParam, cond.room_id);
    });
    const existingMessages = await qb.getMany();

    const existingMap = new Map<string, Message>();
    for (const m of existingMessages) {
      const key = `${m.time_raw}-${m.room_id ?? ''}`;
      existingMap.set(key, m);
    }

    const toUpdate: Message[] = [];
    const toCreate: Message[] = [];

    for (const dto of dtos) {
      const time = new Date(dto.time);
      const key = `${dto.time}-${dto.room_id ?? ''}`;
      const entity = this.repo.create({
        ...dto,
        time,
        time_raw: dto.time,
        message: JSON.stringify(dto.message),
      });

      const existing = existingMap.get(key);
      if (existing) {
        entity.id = existing.id;
        toUpdate.push(entity);
      } else {
        toCreate.push(entity);
      }
    }

    const updated = await this.repo.save(toUpdate);
    const created = await this.repo.save(toCreate);

    return [...updated, ...created];
  }

  async sendMessage(data: SendMessageDto) {
    const conversation = await this.conversationRepo.findOne({
      where: { id: data.conversation_id },
    });

    if (!conversation)
      throw new NotFoundException(
        AppResponse.toResponse(null, {
          message: SystemLang.getText('messages', 'not_found'),
          status_code: HttpStatus.NOT_FOUND,
        }),
      );

    this.event.sendEvent(MessagesEventService.EVENTS.SEND_MESSAGE, data);

    return AppResponse.toResponse({
      conversation,
      data,
    });
  }

  async replyMessage(data: ReplyMessageDto) {
    const conversation = await this.conversationRepo.findOne({
      where: {
        id: data.conversation_id,
        messages: { time_raw: data.time },
      },
      relations: { messages: true },
    });

    if (!conversation)
      throw new NotFoundException(
        AppResponse.toResponse(null, {
          message: SystemLang.getText('messages', 'not_found'),
          status_code: HttpStatus.NOT_FOUND,
        }),
      );

    this.event.sendEvent(MessagesEventService.EVENTS.REPLY_MESSAGE, data);

    return AppResponse.toResponse({
      conversation,
      data,
    });
  }

  async createAndSendToZulip({ data }: CreateBulkMessageDto) {
    // Lọc message hợp lệ
    const filteredData = data.filter((message) => message?.message?.length);

    // Map thành danh sách Promise
    const promises = filteredData.map(async (mes) => {
      try {
        const result = await this.create(mes);
        return result ?? null; // nếu result là undefined/null
      } catch (error) {
        this.logger.error(`Failed to create message`, { mes, error });
        return null; // Đánh dấu lỗi, không throw
      }
    });

    // Chạy tất cả promise cùng lúc
    const results = await Promise.allSettled(promises);

    // Lấy ra các kết quả thành công và không null
    return results
      .filter((res) => res.status === 'fulfilled' && res.value)
      .map((res) => (res as PromiseFulfilledResult<any>).value);
  }
}
