const { PaymentMethod } = require("../../models");
const { Op } = require("sequelize");
const { ValidationError, NotFoundError, ConflictError } = require("../../utils/errors");
const { baseUrl, parseBoolean } = require("../../utils/helpers");

const safeJson = (val, fieldName) => {
  if (val === null || val === undefined || val === "") return null;
  if (typeof val === "object") return val; // already parsed
  if (typeof val !== "string") throw new ValidationError(`${fieldName} must be a JSON object or JSON string`);
  try {
    const parsed = JSON.parse(val);
    if (parsed && typeof parsed === "object") return parsed;
    throw new Error("not-object");
  } catch {
    throw new ValidationError(`${fieldName} must be valid JSON`);
  }
};

const safeReturnJson = (val) => {
  if (val === null || val === undefined || val === "") return null;
  if (typeof val === "object") return val; // already parsed
  if (typeof val !== "string") return {};
  try {
    const parsed = JSON.parse(val);
    if (parsed && typeof parsed === "object") return parsed;
    return {};
  } catch {
    return {};
  }
};
const safeFileUrl = (file) => {
  if (!file) return null;

  return {
    name: file.originalname,
    filename: file.filename,
    path: `/uploads/payment-methods/${file.filename}`,
    mimeType: file.mimetype,
    size: file.size,
  };
};


class adminPaymentMethodsService {
  async getPaymentMethods(query = {}) {
    const { q, type, isActive, page = 1, limit = 20 } = query;

    const pageNum = Math.max(parseInt(page, 10) || 1, 1);
    const limitNum = Math.min(Math.max(parseInt(limit, 10) || 20, 1), 100);
    const offset = (pageNum - 1) * limitNum;

    const where = {};

    if (type) where.type = String(type).toLowerCase();
    if (isActive === "true") where.isActive = true;
    if (isActive === "false") where.isActive = false;

    if (q && String(q).trim()) {
      const term = String(q).trim();
      where[Op.or] = [
        { name: { [Op.like]: `%${term}%` } },
        { symbol: { [Op.like]: `%${term}%` } },
        { slug: { [Op.like]: `%${term}%` } },
        { reference: { [Op.like]: `%${term}%` } },
      ];
    }

    const { rows, count } = await PaymentMethod.findAndCountAll({
      where,
      order: [["created_at", "DESC"]],
      limit: limitNum,
      offset,
    });

    return {
      count,
      page: pageNum,
      limit: limitNum,
      paymentMethods: rows.map(p => ({
        auto: p.auto,
        config: safeReturnJson(p.config),
        created_at: p.created_at,
        depositCharge: p.depositCharge,
        details: safeReturnJson(p.details),
        digit: p.digit,
        exchange: p.exchange,
        id: p.id,
        isActive: p.isActive,
        logo: `${baseUrl}/uploads/deposit-img/${p.logo}`,
        maximum: p.maximum,
        minimum: p.minimum,
        name: p.name,
        qrCode: p.qrCode,
        reference: p.reference,
        showSupport: p.showSupport,
        slug: p.slug,
        symbol: p.symbol,
        type: p.type,
      })),
    };
  }

  async getPaymentMethodById(paymentMethodId) {
    const pm = await PaymentMethod.findByPk(paymentMethodId);
    if (!pm) throw new NotFoundError("Payment method not found");
    return { paymentMethod: pm };
  }

  async createPaymentMethod(data , file) {
    
    const payload = this._sanitizePayload(data);
   
    if (file) {
      payload.logo = file;
    }
   
    // slug unique
  
    const created = await PaymentMethod.create(payload);
    return created;
  }

  async updatePaymentMethod(paymentMethodId, data, file) {
    const pm = await PaymentMethod.findByPk(paymentMethodId);
    if (!pm) throw new NotFoundError("Payment method not found");
    
    const payload = this._sanitizePayload(data, { partial: true });

    if (file) {
      payload.logo = file;
    }

    // if slug changed -> unique check
    /* if (payload.slug && payload.slug !== pm.slug) {
      const exists = await PaymentMethod.findOne({
        where: { slug: payload.slug, id: { [Op.ne]: pm.id } },
      });
      if (exists) throw new ConflictError("Slug already exists");
    } */

    await pm.update(payload);
    return pm;
  }

  async deletePaymentMethod(paymentMethodId) {
    const pm = await PaymentMethod.findByPk(paymentMethodId);
    if (!pm) throw new NotFoundError("Payment method not found");

    await pm.destroy();
    return { deleted: true };
  }

  _sanitizePayload(data, { partial = false } = {}) {
    const out = {};

    const set = (key, val) => {
      if (val === undefined) return;
      out[key] = val;
    };

    // required on create
    const requireOnCreate = (key) => {
      if (!partial && (data?.[key] === undefined || data?.[key] === null || data?.[key] === "")) {
        throw new ValidationError(`${key} is required`);
      }
    };

    requireOnCreate("slug");
    //requireOnCreate("logo");
    requireOnCreate("name");
    requireOnCreate("symbol");

    if (!partial) {
      set("slug", String(data.slug).trim());
      if (data.logo !== undefined && data.logo !== "") set("logo", String(data.logo).trim());
      set("name", String(data.name).trim());
      set("symbol", String(data.symbol).trim());
    } else {
      if (data.slug !== undefined) set("slug", String(data.slug).trim());
      if (data.logo !== undefined && data.logo !== "") set("logo", String(data.logo).trim());
      if (data.name !== undefined) set("name", String(data.name).trim());
      if (data.symbol !== undefined) set("symbol", String(data.symbol).trim());
    }

    if (data.type !== undefined) {
      const t = String(data.type).toLowerCase();
      if (!["crypto", "fiat"].includes(t)) throw new ValidationError("type must be crypto or fiat");
      set("type", t);
    }

    // booleans
    if (data.auto !== undefined) set("auto", parseBoolean(data.auto));
    if (data.exchange !== undefined) set("exchange", parseBoolean(data.exchange));
    if (data.qrCode !== undefined) set("qrCode", parseBoolean(data.qrCode));
    if (data.isActive !== undefined) set("isActive", parseBoolean(data.isActive));
    if (data.showSupport !== undefined) set("showSupport", parseBoolean(data.showSupport));

    // numbers (decimals)
    const decFields = ["depositCharge", "minimum", "maximum"];
    for (const f of decFields) {
      if (data[f] !== undefined) {
        if (data[f] === "" || data[f] === null) {
          set(f, null);
        } else {
          const n = Number(data[f]);
          if (!Number.isFinite(n) || n < 0) throw new ValidationError(`${f} must be a valid number >= 0`);
          set(f, n);
        }
      }
    }

    if (data.digit !== undefined) {
      if (data.digit === "" || data.digit === null) set("digit", 2);
      else {
        const d = parseInt(data.digit, 10);
        if (!Number.isFinite(d) || d < 0 || d > 18) throw new ValidationError("digit must be between 0 and 18");
        set("digit", d);
      }
    }

    // JSON fields (accept object or JSON string)
    if (data.slug === 'crypto-deposit') {
      if (data.wallet !== undefined) set("details", safeJson({ wallet: data.wallet }, "details"))
    } else if (data.slug === 'bank-deposit') {
      const details = {};
      if (data.bank_name !== undefined) details.bank_name = data.bank_name;
      if (data.account_name !== undefined) details.account_name = data.account_name;
      if (data.account_number !== undefined) details.account_number = data.account_number;
      if (data.routing_number !== undefined) details.routing_number = data.routing_number;
      if (data.swift_code !== undefined) details.swift_code = data.swift_code;
      if (data.bank_address !== undefined) details.bank_address = data.bank_address;
      if (details) set("details", safeJson(details, "details"))
    } else {
      if (data.wallet !== undefined) set("details", safeJson({ details: data.wallet }, "details"))
    }
    //if (data.details !== undefined) set("details", safeJson(data.details, "details"));
    //if (data.config !== undefined) set("config", safeJson(data.config, "config"));

    return out;
  }
}

module.exports = new adminPaymentMethodsService();