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

class adminWithdrawalMethodsService {
  async getWithdrawalMethods(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 (typeof isActive !== "undefined" && isActive !== "") {
      if (String(isActive) === "true") where.isActive = true;
      if (String(isActive) === "false") where.isActive = false;
    }

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

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

    return {
      count,
      page: pageNum,
      limit: limitNum,
      methods: rows,
    };
  }

  async getWithdrawalMethodById(id) {
    const method = await WithdrawalMethod.findByPk(id);
    if (!method) throw new NotFoundError("Withdrawal method not found");
    return { method };
  }

  async createWithdrawalMethod(data, adminId = null) {
    const payload = this._normalizePayload(data, true);

    // slug unique check
    const exists = await WithdrawalMethod.findOne({ where: { slug: payload.slug } });
    if (exists) throw new ConflictError("Slug already exists");

    const created = await WithdrawalMethod.create({
      ...payload,
      // optional audit metadata if you store it:
      // createdBy: adminId
    });

    return created;
  }

  async updateWithdrawalMethod(id, data, adminId = null) {
    const method = await WithdrawalMethod.findByPk(id);
    if (!method) throw new NotFoundError("Withdrawal method not found");

    const payload = this._normalizePayload(data, false);

    if (payload.slug && payload.slug !== method.slug) {
      const exists = await WithdrawalMethod.findOne({
        where: { slug: payload.slug, id: { [Op.ne]: id } },
      });
      if (exists) throw new ConflictError("Slug already exists");
    }

    await method.update({
      ...payload,
      // updatedBy: adminId
    });

    return method;
  }

  async deleteWithdrawalMethod(id, adminId = null) {
    const method = await WithdrawalMethod.findByPk(id);
    if (!method) throw new NotFoundError("Withdrawal method not found");

    await method.destroy();
    return true;
  }

  async toggleWithdrawalMethod(id, adminId = null) {
    const method = await WithdrawalMethod.findByPk(id);
    if (!method) throw new NotFoundError("Withdrawal method not found");

    await method.update({ isActive: !Boolean(method.isActive) });
    return method;
  }

  _normalizePayload(data = {}, isCreate = false) {
    const d = data || {};

    const pickStr = (k) => (typeof d[k] === "string" ? d[k].trim() : d[k]);
    const toNumOrNull = (v) => {
      if (v === "" || v === null || typeof v === "undefined") return null;
      const n = Number(v);
      if (!Number.isFinite(n)) return null;
      return n;
    };

    const payload = {};

    // required on create
    const required = ["slug", "logo", "type", "name", "symbol", "duration"];
    if (isCreate) {
      for (const k of required) {
        const v = pickStr(k);
        if (!v) throw new ValidationError(`${k} is required`);
        payload[k] = v;
      }
    } else {
      // optional on update
      for (const k of ["slug", "logo", "type", "name", "symbol", "duration"]) {
        if (typeof d[k] !== "undefined") payload[k] = pickStr(k);
      }
    }

    if (typeof d.minimum !== "undefined") payload.minimum = toNumOrNull(d.minimum);
    if (typeof d.maximum !== "undefined") payload.maximum = toNumOrNull(d.maximum);
    if (typeof d.chargesFixed !== "undefined") payload.chargesFixed = toNumOrNull(d.chargesFixed);
    if (typeof d.chargesPercentage !== "undefined") payload.chargesPercentage = toNumOrNull(d.chargesPercentage);

    if (typeof d.isActive !== "undefined") {
      if (d.isActive === true || d.isActive === false) payload.isActive = d.isActive;
      else payload.isActive = String(d.isActive) === "true";
    }

    // type validation (your enum is 'crypto' | 'fiat')
    if (payload.type) {
      const t = String(payload.type).toLowerCase();
      if (!["crypto", "fiat"].includes(t)) throw new ValidationError("type must be crypto or fiat");
      payload.type = t;
    }

    return payload;
  }
}

module.exports = new adminWithdrawalMethodsService();