// services/admin/supportService.js
const { Op } = require("sequelize");
const { Ticket, TicketMessage, Admin, User, Notification } = require("../../models");
const { generateTicketId } = require("../../utils/helpers");
const { NotFoundError, BadRequestError } = require("../../utils/errors");

class SupportService {
  // ─── helpers ──────────────────────────────────────────────────────────────

  /**
   * Send an in-app (and optionally email/push) notification to the ticket owner.
   * Silently swallows errors so a notification failure never breaks the API.
   */
  async _notifyUser(ticket, { title, message, priority = "medium" } = {}) {
    try {
      await Notification.create({
        userId: ticket.userId,
        type: "support",
        title,
        message,
        icon: "MessageCircle",
        color: "indigo",
        link: `/support/tickets/${ticket.ticketId}`,
        priority,
        channels: { inApp: true, email: true, push: false, sms: false },
        relatedId: ticket.id,
        relatedType: "Ticket",
        metadata: {
          ticketId: ticket.ticketId,
          ticketSubject: ticket.subject,
        },
      });
    } catch (err) {
      // Log but don't throw — notification failure must never break the response
      console.error("[SupportService._notifyUser] Failed to create notification:", err?.message);
    }
  }

  // ─── tickets ──────────────────────────────────────────────────────────────

  async getTickets(filters = {}) {
    const { category, status, priority, search, page = 1, limit = 20 } = filters;

    const where = {};
    if (category) where.category = category;
    if (status) where.status = status;
    if (priority) where.priority = priority;
    if (search) {
      where[Op.or] = [
        { subject: { [Op.iLike]: `%${search}%` } },
        { ticketId: { [Op.iLike]: `%${search}%` } },
      ];
    }

    const { count, rows } = await Ticket.findAndCountAll({
      where,
      include: [
        {
          model: User,
          attributes: ["id", "email", "firstName" , "lastName"],
        },
      ],
      order: [["createdAt", "DESC"]],
      limit: Number(limit),
      offset: (Number(page) - 1) * Number(limit),
    });

    return {
      tickets: rows,
      pagination: {
        total: count,
        page: Number(page),
        limit: Number(limit),
        totalPages: Math.ceil(count / limit),
      },
    };
  }

  async getTicketById(ticketId) {
    const ticket = await Ticket.findOne({
      where: { id: ticketId },
      include: [
        {
          model: TicketMessage,
          as: "messages",
          order: [["createdAt", "ASC"]],
        },
        {
          model: User,
          attributes: ["id", "email", "firstName", "lastName"],
        },
      ],
    });

    if (!ticket) throw new NotFoundError("Ticket not found");
    return ticket;
  }

  async createTicket(data) {
    const { userId, category, subcategory, subject, description, priority, orderId, transactionId, attachments } = data;

    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");

    const ticket = await Ticket.create({
      ticketId: generateTicketId(),
      userId,
      category,
      subcategory,
      subject,
      description,
      priority: priority || "medium",
      orderId,
      transactionId,
      attachments: attachments || [],
    });

    // Initial message mirrors the description
    await TicketMessage.create({
      ticketId: ticket.id,
      senderId: userId,
      senderType: "user",
      senderName: user.fullName || user.email,
      message: description,
      attachments: attachments || [],
    });

    return ticket;
  }

  /**
   * Admin replies to a ticket → closes the ticket + notifies the user.
   */
  async replyToTicket(adminId, adminName, ticketId, message, attachments = [], isInternal = false) {
    const ticket = await this.getTicketById(ticketId);

    if (ticket.status === "closed") {
      throw new BadRequestError("Cannot reply to a closed ticket");
    }

    // Create the reply message
    const ticketMessage = await TicketMessage.create({
      ticketId: ticket.id,
      senderId: adminId,
      senderType: "support",
      senderName: adminName,
      message,
      attachments,
      isInternal: !!isInternal,
    });

    // Replying automatically closes the ticket (as requested)
    await ticket.update({
      lastReplyBy: "support",
      lastReplyAt: new Date(),
    });

    // Notify the user (only for non-internal notes)
    if (!isInternal) {
      await this._notifyUser(ticket, {
        title: "Support replied to your ticket",
        message: `Your ticket "${ticket.subject}" has been answered. Tap to view the reply.`,
        priority: ticket.priority === "urgent" ? "high" : "medium",
      });
    }

    return ticketMessage;
  }

  async closeTicket(ticketId) {
    const ticket = await this.getTicketById(ticketId);

    if (ticket.status === "closed") throw new BadRequestError("Ticket is already closed");

    await ticket.update({ status: "closed", closedAt: new Date() });

    await this._notifyUser(ticket, {
      title: "Your support ticket has been closed",
      message: `Ticket "${ticket.subject}" (${ticket.ticketId}) was closed by our support team.`,
    });

    return ticket;
  }

  async reopenTicket(ticketId) {
    const ticket = await this.getTicketById(ticketId);

    if (ticket.status !== "closed") throw new BadRequestError("Only closed tickets can be reopened");

    await ticket.update({ status: "open", closedAt: null });

    await this._notifyUser(ticket, {
      title: "Your support ticket has been reopened",
      message: `Ticket "${ticket.subject}" (${ticket.ticketId}) has been reopened.`,
    });

    return ticket;
  }

  async updateTicketStatus(ticketId, status) {
    const ticket = await this.getTicketById(ticketId);
    const previousStatus = ticket.status;
    await ticket.update({ status });

    // Notify user of meaningful status changes
    const notifyStatuses = ["in_progress", "resolved", "closed"];
    if (notifyStatuses.includes(status) && status !== previousStatus) {
      const labels = {
        in_progress: { title: "Your ticket is being worked on", msg: `Support is actively working on "${ticket.subject}".` },
        resolved: { title: "Your ticket has been resolved", msg: `Your ticket "${ticket.subject}" has been marked resolved.` },
        closed: { title: "Your ticket has been closed", msg: `Your ticket "${ticket.subject}" has been closed.` },
      };
      const { title, msg } = labels[status];
      await this._notifyUser(ticket, { title, message: msg });
    }

    return ticket;
  }

  async updateTicketPriority(ticketId, priority) {
    const ticket = await this.getTicketById(ticketId);
    await ticket.update({ priority });
    return ticket;
  }

  async deleteTicket(ticketId) {
    const ticket = await this.getTicketById(ticketId);
    // Cascade deletes messages if FK constraint is set; otherwise destroy manually
    await TicketMessage.destroy({ where: { ticketId: ticket.id } });
    await ticket.destroy();
  }

  async deleteTicketMessage(ticketId, messageId) {
    // Ensure ticket exists
    await this.getTicketById(ticketId);

    const msg = await TicketMessage.findOne({ where: { id: messageId, ticketId } });
    if (!msg) throw new NotFoundError("Message not found");
    await msg.destroy();
  }

  async rateTicket(ticketId, rating, comment = null) {
    const ticket = await this.getTicketById(ticketId);

    if (!["resolved", "closed"].includes(ticket.status)) {
      throw new BadRequestError("Can only rate resolved or closed tickets");
    }
    if (ticket.rating) {
      throw new BadRequestError("Ticket has already been rated");
    }

    await ticket.update({ rating, ratingComment: comment });
    return ticket;
  }

  // ─── faq ──────────────────────────────────────────────────────────────────

  async getFAQCategories() {
    return [
      { id: "account", name: "Account", icon: "User", count: 12 },
      { id: "trading", name: "Trading", icon: "TrendingUp", count: 18 },
      { id: "deposits", name: "Deposits", icon: "Download", count: 8 },
      { id: "withdrawals", name: "Withdrawals", icon: "Upload", count: 10 },
      { id: "security", name: "Security", icon: "Shield", count: 15 },
      { id: "verification", name: "Verification", icon: "CheckCircle", count: 6 },
    ];
  }

  async searchFAQ(queryStr) {
    const faqs = [
      { id: 1, category: "account", question: "How do I create an account?", answer: "Click Sign Up and follow the registration process." },
      { id: 2, category: "account", question: "How do I reset my password?", answer: "Click Forgot Password on the login page." },
      { id: 3, category: "trading", question: "What are trading fees?", answer: "Trading fees are 0.1% for spot and 0.04% for futures." },
      { id: 4, category: "deposits", question: "How long do deposits take?", answer: "Deposits are credited after required confirmations." },
      { id: 5, category: "withdrawals", question: "What is the minimum withdrawal?", answer: "Minimum withdrawal varies by currency." },
      { id: 6, category: "security", question: "How do I enable 2FA?", answer: "Go to Security Settings and click Enable 2FA." },
    ];
    if (!queryStr) return faqs;
    const q = queryStr.toLowerCase();
    return faqs.filter((f) => f.question.toLowerCase().includes(q) || f.answer.toLowerCase().includes(q));
  }

  // ─── stats ────────────────────────────────────────────────────────────────

  async getTicketStats() {
    const [total, open, pending, inProgress, resolved, closed] = await Promise.all([
      Ticket.count(),
      Ticket.count({ where: { status: "open" } }),
      Ticket.count({ where: { status: "pending" } }),
      Ticket.count({ where: { status: "in_progress" } }),
      Ticket.count({ where: { status: "resolved" } }),
      Ticket.count({ where: { status: "closed" } }),
    ]);

    return { total, open, pending, inProgress, resolved, closed };
  }
}

module.exports = new SupportService();