import axios from "axios";
import dotenv from "dotenv";

dotenv.config();

const PSB_BASE_URL = process.env.PSB_BASE_URL;

// Shared axios instance with a default timeout — a hung 9PSB call must not hold an
// Express request (and its connection) open indefinitely.
const http = axios.create({ timeout: 15000 });

class PsbService {
  static async authenticate() {
    const url = `${process.env.PSB_BASE_URL}/virtualaccount/authenticate`;

    try {
      const response = await http.post(url, {
        publickey: process.env.PSB_PUBLIC_KEY,
        privatekey: process.env.PSB_PRIVATE_KEY,
      });

      const { access_token, expires_in } = response.data;
      console.log("✅ Authenticated successfully. Token expires in", expires_in, "seconds");

      return access_token;
    } catch (error) {
      console.error("❌ Failed to authenticate with PSB:", error.response?.data || error.message);
      throw error;
    }
  }

  static async createStaticAccount({ reference, amount, description, name }) {
    try {
      const token = await this.authenticate();

      const url = `${PSB_BASE_URL}/virtualaccount/create`;

      const payload = {
        transaction: {
          reference,
        },
        order: {
          amount,
          currency: "NGN",
          description,
          country: "NGA",
          amounttype: "ANY",
        },
        customer: {
          account: {
            name,
            type: "STATIC",
          },
        },
      };

      const response = await axios.post(url, payload, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
      });

      return response.data;
    } catch (error) {
      if (process.env.NODE_ENV !== "production") {
        console.warn(`⚠️ 9PSB API Account Creation bypassed in DEV mode (${error.response?.data?.message || error.message}). Generated mock account.`);
        const mockAccountNumber = "99" + Math.floor(10000000 + Math.random() * 90000000).toString();
        return {
          customer: {
            account: {
              number: mockAccountNumber,
            },
          },
        };
      }

      console.error(
        "❌ 9PSB Create Account Error:",
        error.response?.data || error.message
      );
      throw error;
    }
  }

  static async authenticateWaas9PSB() {
    const url = `${process.env.PSB_WAAS_BASE_URL}/authenticate`;

    const payload = {
      username: process.env.PSB_WAAS_USERNAME,
      password: process.env.PSB_WAAS_PASSWORD,
      clientId: process.env.PSB_WAAS_CLIENT_ID,
      clientSecret: process.env.PSB_WAAS_CLIENT_SECRET,
    };

    try {
      const response = await http.post(
        `${url}`,
        payload,
        {
          headers: {
            "Content-Type": "application/json",
          },
        }
      );

      return response.data; // expected to contain token
    } catch (error) {
      console.error("❌ 9PSB WAAS Authentication Failed:", error.response?.data || error.message);
      throw error;
    }
  }

  static extractToken = (authResp) => {
    if (!authResp) return null;
    return authResp.accessToken || authResp.access_token || authResp.accessToken?.token || null;
  };

  static async getBanks(merchantID) {
    const merchant = merchantID || "AIRPERO";

    const authResp = await this.authenticateWaas9PSB();
    let token = this.extractToken(authResp);

    if (!token) {
      throw new Error("Could not obtain WAAS access token from authenticateWaas9PSB response");
    }

    const url = `${process.env.PSB_WAAS_BASE_URL}/get_banks`;
    const payload = { merchant };

    try {
      const res = await http.post(url, payload, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        timeout: 10000,
      });

      return res.data;
    } catch (error) {
      const status = error.response?.status;
      if (status === 401) {
        console.warn("⚠️ WAAS token unauthorized, trying to re-authenticate and retry...");
        try {
          const newAuthResp = await this.authenticateWaas9PSB();
          token = this.extractToken(newAuthResp);
          if (!token) throw new Error("Re-authenticate returned no token");

          const retry = await http.post(url, payload, {
            headers: {
              "Content-Type": "application/json",
              Authorization: `Bearer ${token}`,
            },
            timeout: 10000,
          });

          return retry.data;
        } catch (retryErr) {
          console.error("❌ Retry after re-auth failed:", retryErr.response?.data || retryErr.message);
          throw retryErr;
        }
      }
      console.error("❌ getBanks error:", error.response?.data || error.message);
      throw error;
    }
  }

  static async otherBanksEnquiry(payload, merchantID) {
    try {
      const authResp = await this.authenticateWaas9PSB();
      let token = this.extractToken(authResp);
      if (!token) throw new Error("Failed to get WAAS token");

      const url = `${process.env.PSB_WAAS_BASE_URL}/other_banks_enquiry`;
      const merchantCode = merchantID || "AIRPERO";

      const response = await http.post(url, payload, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
          "X-Merchant-Code": merchantCode,
        },
      });

      return response.data;
    } catch (error) {
      console.error(
        "❌ Other Banks Enquiry failed:",
        error.response?.data || error.message
      );
      throw error;
    }
  }

  static async walletOtherBanks(payload, merchantID) {
    const merchantCode = merchantID || "AIRPERO";
    const authResp = await this.authenticateWaas9PSB();
    let token = this.extractToken(authResp);
    if (!token) throw new Error("Failed to get WAAS token");

    const url = `${process.env.PSB_WAAS_BASE_URL}/wallet_other_banks`;
    try {
      const response = await http.post(url, payload, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
      });
      return response.data;
    } catch (error) {
      if (error.response?.status === 401) {
        console.warn("⚠️ WAAS token unauthorized, retrying wallet_other_banks...");
        const newAuthResp = await this.authenticateWaas9PSB();
        token = this.extractToken(newAuthResp);
        if (!token) throw new Error("Re-authenticate returned no token");

        const retry = await http.post(url, payload, {
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${token}`,
            "X-Merchant-Code": merchantCode,
          },
        });
        return retry.data;
      }
      console.log("❌ walletOtherBanks failed:", error.response?.data || error.message);
      throw error;
    }
  }

  static async walletRequery(payload, merchantID) {
    const merchantCode = merchantID || "AIRPERO";
    const authResp = await this.authenticateWaas9PSB();
    let token = this.extractToken(authResp);
    if (!token) throw new Error("Failed to get WAAS token");

    const url = `${process.env.PSB_WAAS_BASE_URL}/wallet_requery`;
    try {
      const response = await http.post(url, payload, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
          "X-Merchant-Code": merchantCode,
        },
      });
      return response.data;
    } catch (error) {
      if (error.response?.status === 401) {
        console.warn("⚠️ WAAS token unauthorized, retrying wallet_requery...");
        const newAuthResp = await this.authenticateWaas9PSB();
        token = this.extractToken(newAuthResp);
        if (!token) throw new Error("Re-authenticate returned no token");

        const retry = await http.post(url, payload, {
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${token}`,
            "X-Merchant-Code": merchantCode,
          },
        });
        return retry.data;
      }
      console.error("❌ walletRequery failed:", error.response?.data || error.message);
      throw error;
    }
  }
}

export default PsbService;