import db from "../models/index.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import crypto from "crypto";
import { v4 as uuidv4 } from "uuid";
import { logAudit } from "../utils/auditLogger.js";
import { issueOtp, verifyOtp } from "../utils/otpService.js";
import { 
  sendPasswordResetEmail, 
  sendMerchantWelcomeEmail, 
  sendNewDeviceLoginEmail, 
  sendPasswordChangedEmail 
} from "../utils/emailService.js";
import PsbService from "../thirdParties/psbService.js";

const { Merchant } = db;

class MerchantAuthController {
  /**
   * Merchant Registration (Self-service)
   */
  static async register(req, res) {
    try {
      const { businessName, email, password, preview } = req.body;

      if (!businessName || !email || !password) {
        return res.status(400).json({ success: false, message: "Missing required fields (businessName, email, password)" });
      }

      const existing = await Merchant.findOne({ where: { email } });
      if (existing) {
        return res.status(400).json({ success: false, message: "Merchant with this email already exists" });
      }

      const publicKey = `pk_${uuidv4().replace(/-/g, "")}`;
      const privateKeyPlain = `sk_${uuidv4().replace(/-/g, "")}`;
      const hashedPrivateKey = await bcrypt.hash(privateKeyPlain, 10);
      const hashedPassword = await bcrypt.hash(password, 10);

      const merchant = await Merchant.create({
        businessName,
        preview: preview || businessName,
        email,
        password: hashedPassword,
        publicKey,
        privateKey: hashedPrivateKey,
        balance: 0,
        status: "active"
      });

      // Sandbox Mode default: Generate test mock account number for new registration
      const sandboxAccount = `99${Math.floor(10000000 + Math.random() * 90000000)}`;
      await merchant.update({
        merchantAccountNumber: sandboxAccount,
        bankName: "9PSB (Sandbox Test)"
      });

      logAudit({
        userId: merchant.id,
        userType: "merchant",
        action: "MERCHANT_REGISTER",
        details: { businessName },
        req
      });

      // Send welcome email (non-blocking) — API keys are never emailed; merchant retrieves them from the dashboard
      sendMerchantWelcomeEmail(merchant.email, {
        businessName: merchant.businessName,
        email: merchant.email,
        accountNumber: merchant.merchantAccountNumber,
      }).catch(() => {});

      return res.status(201).json({
        success: true,
        message: "Merchant registered successfully with API Keys",
        data: {
          id: merchant.id,
          businessName: merchant.businessName,
          email: merchant.email,
          merchantAccountNumber: merchant.merchantAccountNumber,
          publicKey: merchant.publicKey,
          privateKey: privateKeyPlain,
        },
      });
    } catch (error) {
      console.error("❌ Merchant register error:", error);
      return res.status(500).json({
        success: false,
        message: "Failed to register merchant",
        error: error.message,
      });
    }
  }

  /**
   * Merchant Login for Dashboard
   * If 2FA is enabled, returns a temporary session for OTP verification instead of a full JWT.
   */
  static async login(req, res) {
    try {
      const { email, password } = req.body;
      const merchant = await Merchant.findOne({ where: { email } });

      if (!merchant) {
        return res.status(401).json({ success: false, message: "Invalid credentials" });
      }

      if (merchant.status === "suspended") {
        return res.status(403).json({ success: false, message: "Account blocked: Please reach out to support" });
      }

      if (merchant.status !== "active") {
        return res.status(401).json({ success: false, message: "Merchant account is inactive" });
      }

      const isMatch = await bcrypt.compare(password, merchant.password);
      if (!isMatch) {
        logAudit({ userId: merchant.id, userType: "merchant", action: "MERCHANT_LOGIN_FAILED", req });
        return res.status(401).json({ success: false, message: "Invalid credentials" });
      }

      // If 2FA is enabled, do not issue the full token yet
      if (merchant.twoFaEnabled) {
        const { otpId } = await issueOtp(
          merchant.id,
          "merchant",
          "login",
          merchant.email,
          merchant.businessName
        );

        logAudit({ userId: merchant.id, userType: "merchant", action: "MERCHANT_LOGIN_2FA_INITIATED", req });

        return res.status(200).json({
          success: true,
          requiresOtp: true,
          otpId,
          message: "OTP sent to your registered email address. Please verify to complete login.",
        });
      }

      // 2FA not enabled — issue full JWT immediately
      const token = jwt.sign(
        { id: merchant.id, role: "merchant" },
        process.env.JWT_SECRET || "airpero_super_secret_key",
        { expiresIn: "1d" }
      );

      // Check for new device / IP
      const currentIp = req.ip || req.connection.remoteAddress || "Unknown";
      if (merchant.lastLoginIp && merchant.lastLoginIp !== currentIp) {
        sendNewDeviceLoginEmail(merchant.email, merchant.businessName, currentIp).catch(() => {});
      }

      merchant.lastLoginIp = currentIp;
      merchant.lastActivityAt = new Date();
      await merchant.save();

      logAudit({ userId: merchant.id, userType: "merchant", action: "MERCHANT_LOGIN", req });

      return res.status(200).json({
        success: true,
        message: "Login successful",
        token,
        merchant: {
          id: merchant.id,
          businessName: merchant.businessName,
          preview: merchant.preview,
          email: merchant.email,
          twoFaEnabled: merchant.twoFaEnabled,
        },
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Verify OTP after a 2FA login challenge.
   * On success, issues the full JWT.
   */
  static async verifyLoginOtp(req, res) {
    try {
      const { otpId, code } = req.body;

      if (!otpId || !code) {
        return res.status(400).json({ success: false, message: "otpId and code are required" });
      }

      const result = await verifyOtp(otpId, code);

      if (!result.success) {
        return res.status(400).json({ success: false, message: result.message });
      }

      const { data: otpRecord } = result;
      const merchant = await Merchant.findByPk(otpRecord.userId);

      if (!merchant) {
        return res.status(404).json({ success: false, message: "Account not found" });
      }

      const token = jwt.sign(
        { id: merchant.id, role: "merchant" },
        process.env.JWT_SECRET || "airpero_super_secret_key",
        { expiresIn: "1d" }
      );

      // Check for new device / IP
      const currentIp = req.ip || req.connection.remoteAddress || "Unknown";
      if (merchant.lastLoginIp && merchant.lastLoginIp !== currentIp) {
        sendNewDeviceLoginEmail(merchant.email, merchant.businessName, currentIp).catch(() => {});
      }

      merchant.lastLoginIp = currentIp;
      merchant.lastActivityAt = new Date();
      await merchant.save();

      logAudit({ userId: merchant.id, userType: "merchant", action: "MERCHANT_LOGIN_2FA_SUCCESS", req });

      return res.status(200).json({
        success: true,
        message: "Login successful",
        token,
        merchant: {
          id: merchant.id,
          businessName: merchant.businessName,
          preview: merchant.preview,
          email: merchant.email,
          twoFaEnabled: merchant.twoFaEnabled,
        },
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Enable or disable 2FA for the authenticated merchant.
   */
  static async toggle2fa(req, res) {
    try {
      const merchant = req.merchant;
      const newState = !merchant.twoFaEnabled;

      await merchant.update({ twoFaEnabled: newState });

      logAudit({
        userId: merchant.id,
        userType: "merchant",
        action: newState ? "MERCHANT_2FA_ENABLED" : "MERCHANT_2FA_DISABLED",
        req
      });

      return res.status(200).json({
        success: true,
        message: `Two-factor authentication has been ${newState ? "enabled" : "disabled"}.`,
        twoFaEnabled: newState,
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Request a password reset link via email.
   */
  static async forgotPassword(req, res) {
    try {
      const { email } = req.body;
      if (!email) {
        return res.status(400).json({ success: false, message: "Email is required" });
      }

      // Always return success to prevent email enumeration
      const merchant = await Merchant.findOne({ where: { email } });
      if (merchant) {
        const resetToken = crypto.randomBytes(32).toString("hex");
        const hashedToken = crypto.createHash("sha256").update(resetToken).digest("hex");
        const expiresAt = new Date(Date.now() + 30 * 60 * 1000); // 30 minutes

        await merchant.update({
          passwordResetToken: hashedToken,
          passwordResetExpires: expiresAt,
        });

        const frontendUrl = process.env.FRONTEND_URL || "https://dashboard.airpero.com";
        const resetUrl = `${frontendUrl}/reset-password?token=${resetToken}&type=merchant`;

        await sendPasswordResetEmail(merchant.email, resetUrl, merchant.businessName);

        logAudit({ userId: merchant.id, userType: "merchant", action: "MERCHANT_PASSWORD_RESET_REQUESTED", req });
      }

      return res.status(200).json({
        success: true,
        message: "If your email is registered, you will receive a password reset link shortly.",
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Complete the password reset using the token from the email link.
   */
  static async resetPassword(req, res) {
    try {
      const { token, newPassword } = req.body;
      if (!token || !newPassword) {
        return res.status(400).json({ success: false, message: "Token and newPassword are required" });
      }

      const hashedToken = crypto.createHash("sha256").update(token).digest("hex");
      const merchant = await Merchant.findOne({
        where: { passwordResetToken: hashedToken },
      });

      if (!merchant || !merchant.passwordResetExpires || new Date() > new Date(merchant.passwordResetExpires)) {
        return res.status(400).json({ success: false, message: "Password reset token is invalid or has expired" });
      }

      const hashedPassword = await bcrypt.hash(newPassword, 10);
      await merchant.update({
        password: hashedPassword,
        passwordResetToken: null,
        passwordResetExpires: null,
      });
      await merchant.save();

      // Send password changed success email
      sendPasswordChangedEmail(merchant.email, merchant.businessName).catch(() => {});

      logAudit({ userId: merchant.id, userType: "merchant", action: "MERCHANT_PASSWORD_RESET_SUCCESS", req });

      return res.status(200).json({
        success: true,
        message: "Password has been reset successfully. You can now log in with your new password.",
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Logged in merchant retrieves their API Keys.
   */
  static async getApiKeys(req, res) {
    try {
      const merchant = req.merchant;
      return res.status(200).json({
        success: true,
        data: {
          publicKey: merchant.publicKey,
          message: "Private key is only shown upon registration or regeneration."
        },
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Regenerate Merchant API Keys
   */
  static async regenerateKeys(req, res) {
    try {
      const merchantId = req.merchant.id;

      const publicKey = `pk_${uuidv4().replace(/-/g, "")}`;
      const privateKeyPlain = `sk_${uuidv4().replace(/-/g, "")}`;
      const hashedPrivateKey = await bcrypt.hash(privateKeyPlain, 10);

      await Merchant.update({
        publicKey,
        privateKey: hashedPrivateKey
      }, { where: { id: merchantId } });

      logAudit({
        userId: merchantId,
        userType: "merchant",
        action: "REGENERATE_API_KEYS",
        req
      });

      return res.status(200).json({
        success: true,
        message: "Keys regenerated successfully",
        data: {
          publicKey,
          privateKey: privateKeyPlain
        }
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Update Merchant Webhook URL
   */
  static async updateWebhookUrl(req, res) {
    try {
      const merchantId = req.merchant.id;
      const { webhookUrl } = req.body;

      await Merchant.update({ webhookUrl }, { where: { id: merchantId } });

      logAudit({
        userId: merchantId,
        userType: "merchant",
        action: "UPDATE_WEBHOOK_URL",
        details: { webhookUrl },
        req
      });

      return res.status(200).json({
        success: true,
        message: "Webhook URL updated successfully",
        data: { webhookUrl },
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }

  /**
   * Update Merchant Profile (Preview name, etc.)
   */
  static async updateMerchantProfile(req, res) {
    try {
      const merchantId = req.merchant.id;
      const { preview, businessName } = req.body;

      const updateData = {};
      if (preview !== undefined) updateData.preview = preview;
      if (businessName !== undefined) updateData.businessName = businessName;

      await Merchant.update(updateData, { where: { id: merchantId } });

      logAudit({
        userId: merchantId,
        userType: "merchant",
        action: "UPDATE_MERCHANT_PROFILE",
        details: updateData,
        req
      });

      return res.status(200).json({
        success: true,
        message: "Profile updated successfully",
        data: updateData,
      });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }
}

export default MerchantAuthController;
