import { Injectable, BadRequestException } from "@nestjs/common";
import { Users, UsersDocument } from "src/schema/users.schema";
import { InjectModel } from "@nestjs/mongoose";
import { Model, Types } from "mongoose";
import { ConfigService } from "@nestjs/config";
import { EmailService } from "src/email/email.service";
import Stripe from "stripe";
import { getCurrencySymbol } from "src/common/constant/currency.constants";
import {
  PaymentDetails,
  PaymentDetailsDocument,
} from "src/schema/user-payment-details.schema";
import { Invoice } from "src/schema/invoice.schema";
import { AlertsService } from "src/alerts/alerts.service";
import { ResponseService } from "src/common/service/response.service";
import { InvoiceStatus } from "src/common/constant/enum.constant";
import { session } from "passport";
import { response, Response } from "express";
@Injectable()
export class StripeService {
  private stripe: Stripe;
  private webhookSecret: string;
  private clientId: string;

  constructor(
    @InjectModel(Users.name) private userModal: Model<UsersDocument>,
    @InjectModel(PaymentDetails.name)
    private paymentDetailsModel: Model<PaymentDetailsDocument>,
    @InjectModel(Invoice.name)
    private invoiceModel: Model<Invoice>,
    private readonly configService: ConfigService,
    private readonly emailService: EmailService,
    private readonly alertService: AlertsService,
    private readonly resService: ResponseService,
  ) {
    const stripeKey = this.configService.get<string>("STRIPE_SECRET_KEY");

    if (!stripeKey) {
      throw new Error("STRIPE_SECRET_KEY is missing in .env");
    }

    this.webhookSecret = this.configService.get<string>(
      "STRIPE_WEBHOOK_SECRET",
    )!;

    this.clientId = this.configService.get<string>("STRIPE_CLIENT_KEY")!;

    this.stripe = new Stripe(stripeKey);
  }

  private async sendInvoicePaidEmails(invoice: any) {
    const client = invoice.clientId;
    const sender = invoice.userId;
    const FRONTEND_URL = process.env.WEBAPP_URL;

    const InvoiceWebUrl = `${FRONTEND_URL}/invoice/${invoice._id}`;

    const currencyCode =
      invoice.currency ?? (invoice.userId as any)?.currency ?? "USD";
    const currencySymbol = getCurrencySymbol(currencyCode);

    const formattedAmount = `${currencySymbol} ${Number(invoice.total).toFixed(
      2,
    )}`;
    // ===== Email to Customer =====
    if (invoice.clientId.email) {
      await this.emailService.sendInvoicePaidEmailToCustomer({
        Customer_Name: client.name,
        sender_email: sender.email,
        Invoice_Number: invoice.invoiceNo,
        Invoice_Amount: formattedAmount,
        Due_Date: invoice.dueDate ?? null,
        Sender_Company_Name: sender?.organizationName ?? "",
        Sender_Name: sender?.firstName ?? "",
        Invoice_View_Url: InvoiceWebUrl ?? "",
        client_email: client.email,
      });
    }

    // ===== Email to Sender =====
    if (invoice.userId.email) {
      await this.emailService.sendInvoicePaidEmailToSender({
        Sender_Name: sender?.organizationName ?? "",
        Invoice_Number: invoice.invoiceNo,
        Invoice_Amount: formattedAmount,
        Due_Date: invoice.dueDate ?? null,
        Invoice_View_Url: InvoiceWebUrl ?? "",
        sender_email: sender.email,
      });
    }
  }

  async createConnectAccount(id: string) {
    try {
      const user = await this.userModal.findById(id).lean();
      const accountParams: Stripe.AccountCreateParams = {
        type: "standard",
        country: user?.countryCode,
        capabilities: {
          card_payments: { requested: true },
          transfers: { requested: true },
        },
      };
      //  Only add email if it exists AND is valid
      if (user?.email && this.isValidEmail(user.email)) {
        accountParams.email = user.email;
      }

      const account = await this.stripe.accounts.create(accountParams);

      await this.paymentDetailsModel.updateOne(
        { userId: new Types.ObjectId(id) },
        {
          $set: { stripeConnectId: account.id },
          $setOnInsert: { createdAt: new Date() },
        },
        { upsert: true },
      );

      return account.id;
    } catch (error) {
      // ✅ THROW, don't return
      throw new BadRequestException(
        error?.message || "Failed to create Stripe account",
      );
    }
  }

  async createOnboardingLink(accountId: string) {
    return this.stripe.accountLinks.create({
      account: accountId,
      refresh_url: process.env.STRIPE_REFRESH_URL!,
      return_url: `${process.env.STRIPE_RETURN_URL}?accountId=${accountId}`,
      type: "account_onboarding",
    });
  }

  constructWebhookEvent(payload: Buffer, signature: string) {
    try {
      return this.stripe.webhooks.constructEvent(
        payload,
        signature,
        this.webhookSecret,
      );
    } catch (err) {
      throw new BadRequestException(`Webhook signature verification failed`);
    }
  }

  async handleAccountUpdated(account: Stripe.Account) {
    const user = await this.paymentDetailsModel
      .findOne({ stripeConnectId: account.id })
      .lean();
    if (!user) return;

    const connected = account.charges_enabled === true;

    await this.paymentDetailsModel.updateOne(
      { userId: user.userId },
      {
        $set: {
          stripeConnected: connected,
          chargesEnabled: account.charges_enabled,
          payoutsEnabled: account.payouts_enabled,
        },
      },
    );
  }

  async disconnectAccount(userId: string) {
    try {
      const user = await this.paymentDetailsModel
        .findOne({ userId: userId })
        .lean();
      if (!user || !user.stripeConnectId) {
        throw new BadRequestException("Stripe account not connected");
      }
      //  Update DB (single query)
      await this.paymentDetailsModel.updateOne(
        { userId: userId },
        {
          $set: {
            stripeConnected: false,
            chargesEnabled: false,
            payoutsEnabled: false,
          },
          $unset: {
            stripeConnectId: "",
          },
        },
      );
    } catch (err) {
      throw new BadRequestException(
        `Failed to disconnect Stripe: ${err.message}`,
      );
    }
  }

  async handleAccountDisconnected(data: any) {
    await this.paymentDetailsModel.updateOne(
      { stripeConnectId: data.object.account },
      {
        $set: {
          stripeConnected: false,
          chargesEnabled: false,
          payoutsEnabled: false,
        },
        $unset: { stripeConnectId: "" },
      },
    );
  }

  /**
   * Fetch latest Stripe account status
   * Source of truth after onboarding
   */
  async getAccountStatusByAccountId(accountId: string) {
    if (!accountId?.startsWith("acct_")) {
      throw new BadRequestException("Invalid Stripe account id");
    }

    // 1️⃣ Fetch account from Stripe
    const account = await this.stripe.accounts.retrieve(accountId);
    const connected = account.charges_enabled === true;
    // 2️⃣ Update DB using stripeAccountId
    await this.paymentDetailsModel.updateOne(
      { stripeConnectId: accountId },
      {
        $set: {
          stripeConnected: connected,
          chargesEnabled: account.charges_enabled,
          payoutsEnabled: account.payouts_enabled,
        },
      },
    );

    // 3️⃣ Return status
    return {
      stripeAccountId: account.id,
      stripeConnected: connected,
      chargesEnabled: account.charges_enabled,
      payoutsEnabled: account.payouts_enabled,
      detailsSubmitted: account.details_submitted,
      requirements: {
        currently_due: account.requirements?.currently_due || [],
        disabled_reason: account.requirements?.disabled_reason || null,
      },
    };
  }

  async createCheckoutSession(invoiceId: string) {
    try {
      const invoice = await this.invoiceModel.findById(invoiceId);

      if (!invoice) {
        throw new BadRequestException("Invoice not found");
      }

      if (invoice.isPaid) {
        throw new BadRequestException("Invoice already paid");
      }

      const userObjectId = new Types.ObjectId(invoice.userId);
      const paymentDetails = await this.paymentDetailsModel.findOne({
        userId: userObjectId,
      });

      console.log(paymentDetails, "payment details");
      if (
        !paymentDetails ||
        !paymentDetails.chargesEnabled ||
        !paymentDetails.payoutsEnabled
      ) {
        throw new BadRequestException(
          "Stripe account is not ready to accept payments",
        );
      }

      const session = await this.stripe.checkout.sessions.create(
        {
          mode: "payment",
          payment_method_types: ["card"],
          line_items: [
            {
              price_data: {
                currency: "AUD",
                product_data: {
                  name: `Invoice #${invoice.invoiceNo}`,
                },
                unit_amount: invoice.total * 100,
              },
              quantity: 1,
            },
          ],
          metadata: {
            invoiceId: invoice._id.toString(),
          },
          success_url: `${process.env.WEBAPP_URL}/payment-success?session_id={CHECKOUT_SESSION_ID}`,
          cancel_url: `${process.env.WEBAPP_URL}/payment-cancel`,
        },
        {
          stripeAccount: paymentDetails.stripeConnectId,
        },
      );

      return { success: true, url: session.url };
    } catch (error) {
      console.log("error during checkout ", error);
      throw new BadRequestException(
        `Failed to create checkout session: ${error.message}`,
      );
    }
  }

  // async handleCheckoutSessionCompleted(session: Stripe.Checkout.Session) {
  //   try {
  //     console.log(session);
  //     if (session.payment_status === "paid") {
  //       const invoiceId = session.metadata?.invoiceId;
  //       const invoice = await this.invoiceModel
  //         .findById(invoiceId)
  //         .populate({
  //           path: "clientId",
  //           select: "name email", // only what you need
  //         })
  //         .populate({
  //           path: "userId",
  //           select: "email organizationName firstName",
  //         });

  //       if (!invoice) {
  //         throw new BadRequestException("Invoice not found");
  //       }

  //       // assuming invoice.clientId exists
  //       const clientId = invoice.clientId;

  //       await this.invoiceModel.updateOne(
  //         { _id: invoiceId },
  //         {
  //           $set: {
  //             isPaid: true,
  //             paidDate: new Date(),
  //             paidByClient: clientId,
  //             stripeSessionId: session.id,
  //           },
  //         },
  //       );
  //     }
  //   } catch (error) {
  //     throw error;
  //   }
  //   const invoiceId = session.metadata?.invoiceId;
  //   if (!invoiceId) {
  //     throw new BadRequestException("Invoice ID not found in metadata");
  //   }
  // }

  async handleCheckoutSessionCompleted(session: Stripe.Checkout.Session) {
    try {
      if (session.payment_status !== "paid") return;

      const invoiceId = session.metadata?.invoiceId;
      if (!invoiceId) {
        throw new BadRequestException("Invoice ID not found in metadata");
      }

      const invoice = await this.invoiceModel
        .findById(invoiceId)
        .populate({
          path: "clientId",
          select: "name email",
        })
        .populate({
          path: "userId",
          select: "email organizationName firstName currency",
        });

      if (!invoice) {
        throw new BadRequestException("Invoice not found");
      }

      // ================= UPDATE INVOICE =================
      invoice.isPaid = true;
      invoice.paidDate = new Date();
      invoice.paidByClient = invoice.clientId;
      invoice.stripeSessionId = session.id;

      await invoice.save();

      // ================= SEND EMAILS =================
      try {
        await this.sendInvoicePaidEmails(invoice);
      } catch (emailError) {
        console.error("Invoice paid email failed:", emailError);
      }

      // ================= SEND ALERT =================
      try {
        await this.alertService.invoicePaid(invoice.userId._id.toString(), {
          _id: invoice._id,
          number: invoice.invoiceNo,
        });
      } catch (alertError) {
        console.error("Invoice paid alert failed:", alertError);
      }
      console.log(invoice, "invoice");

      return {
        _id: invoice._id,
        stripeSessionId: invoice.stripeSessionId,
      };
    } catch (error) {
      console.error("Checkout session handling failed:", error);
      throw error;
    }
  }

  async getPaymentStatus(res: Response, sessionId: string) {
    try {
      if (!sessionId) {
        return this.resService.badRequest(res, "Session ID is required");
      }

      /* ================= FIND INVOICE ================= */
      const invoice = await this.invoiceModel.findOne({
        stripeSessionId: sessionId,
      });

      if (!invoice) {
        return this.resService.notFound(res, "Invoice not found");
      }

      /* ================= GET PAYMENT DETAILS ================= */
      const paymentDetails = await this.paymentDetailsModel.findOne({
        userId: invoice.userId,
      });

      if (!paymentDetails || !paymentDetails.stripeConnectId) {
        return this.resService.badRequest(
          res,
          "Stripe account not connected for this user",
        );
      }

      /* ================= RETRIEVE STRIPE SESSION ================= */
      const session = await this.stripe.checkout.sessions.retrieve(sessionId, {
        stripeAccount: paymentDetails.stripeConnectId,
      });

      if (!session) {
        return this.resService.notFound(res, "Stripe session not found");
      }

      const finalCurrency = invoice.currency || session.currency;

      /* ================= GET CURRENCY SYMBOL ================= */
      const currencySymbol = finalCurrency
        ? getCurrencySymbol(finalCurrency)
        : null;

      /* ================= RESPONSE FORMAT ================= */
      const responseData = {
        sessionId: session.id,
        paymentStatus: session.payment_status, // paid | unpaid | no_payment_required
        status: session.status, // open | complete | expired
        amountTotal: session.amount_total,
        currency: finalCurrency,
        currencySymbol: currencySymbol,
        customerEmail: session.customer_details?.email,
        invoices: invoice,
      };

      return this.resService.success(
        res,
        responseData,
        "Payment status fetched",
      );
    } catch (error) {
      console.error("Get Payment Status Error:", error);
      return this.resService.serverError(
        res,
        "Something went wrong while fetching payment status",
      );
    }
  }

  /**
   * Validate email address format
   */
  private isValidEmail(email: string): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  }
}
