"use strict";

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

const toBool = (v) => v === true || v === "true" || v === 1 || v === "1";
const toNum = (v) => (v === "" || v === null || v === undefined ? null : Number(v));

const allowedDurationTypes = new Set(["hour", "day", "week", "month", "year"]);
const allowedTypes = new Set(["Main", "Promo"]);

function normalizeFeatures(features) {
  if (Array.isArray(features)) return features.map(String).map((s) => s.trim()).filter(Boolean);
  if (typeof features === "string") {
    return features
      .split(",")
      .map((s) => s.trim())
      .filter(Boolean);
  }
  return [];
}

function buildPlanPayload(input = {}) {
  // Map your UI fields to your Sequelize Plan model:
  // - popular -> highlight
  // - users/storage -> limits JSON
  // - period -> durationType (duration defaults to 1)
  const name = (input.name ?? "").toString().trim();
  if (!name) throw new ValidationError("name is required");

  const priceNum = toNum(input.price);
  if (!Number.isFinite(priceNum) || priceNum < 0) throw new ValidationError("price must be a valid number");

  const type = (input.type ?? "Main").toString();
  if (!allowedTypes.has(type)) throw new ValidationError("type must be Main or Promo");

  const durationType = (input.period ?? input.durationType ?? "month").toString();
  if (!allowedDurationTypes.has(durationType)) throw new ValidationError("period must be hour/day/week/month/year");

  const duration = toNum(input.duration ?? 1);
  if (!Number.isFinite(duration) || duration <= 0) throw new ValidationError("duration must be > 0");

  const highlight = toBool(input.popular ?? input.highlight);

  const features = normalizeFeatures(input.features);

  // limits/config store UI strings safely
  const limits = {
    ...(typeof input.limits === "object" && input.limits ? input.limits : {}),
    users: (input.users ?? input?.limits?.users ?? "").toString().trim(),
    storage: (input.storage ?? input?.limits?.storage ?? "").toString().trim(),
  };

  const config =
    typeof input.config === "object" && input.config
      ? input.config
      : {};

  // optional fields
  const tagline = input.tagline ?? null;
  const description = input.description ?? null;

  const status = input.status === undefined ? true : toBool(input.status);

  return {
    name,
    tagline,
    description,
    price: priceNum,
    maxPrice: input.maxPrice ?? null,
    features,
    limits,
    highlight,
    type,
    roi: input.roi ?? null,
    roiType: input.roiType ?? "percent",
    roiInterval: input.roiInterval ?? null,
    duration,
    durationType,
    config,
    status,
  };
}

class adminPlanService {
  async getPlans(query = {}) {
    const { q, status, type, 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 (status !== undefined && status !== "") where.status = toBool(status);
    if (type) where.type = type;

    if (q) {
      where[Op.or] = [
        { id: { [Op.like]: `%${q}%` } },
        { name: { [Op.like]: `%${q}%` } },
      ];
    }

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

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

  async getPlanById(planId) {
    const plan = await Plan.findByPk(planId);
    if (!plan) throw new NotFoundError("Plan not found");
    return { plan };
  }

  async createPlan(data, adminId = null) {
    const payload = buildPlanPayload(data);

    // Optional uniqueness rule (name)
    const exists = await Plan.findOne({ where: { name: payload.name } });
    if (exists) throw new ValidationError("Plan name already exists");

    // Optional audit metadata
    payload.config = {
      ...(payload.config || {}),
      created_by_admin: adminId ?? null,
    };

    const plan = await Plan.create(payload);
    return plan;
  }

  async updatePlan(planId, data, adminId = null) {
    const plan = await Plan.findByPk(planId);
    if (!plan) throw new NotFoundError("Plan not found");

    const payload = buildPlanPayload({ ...plan.toJSON(), ...data });

    // If renaming, prevent duplicates
    if (payload.name && payload.name !== plan.name) {
      const exists = await Plan.findOne({ where: { name: payload.name, id: { [Op.ne]: plan.id } } });
      if (exists) throw new ValidationError("Plan name already exists");
    }

    payload.config = {
      ...(payload.config || {}),
      updated_by_admin: adminId ?? null,
      updated_at_admin: new Date().toISOString(),
    };

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

  async deletePlan(planId, adminId = null) {
    const plan = await Plan.findByPk(planId);
    if (!plan) throw new NotFoundError("Plan not found");

    // Hard delete (change to soft delete if you prefer: await plan.update({ status:false }))
    await plan.destroy();

    return {
      deleted: true,
      planId,
      deleted_by_admin: adminId ?? null,
    };
  }
}

module.exports = new adminPlanService();