import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { Rules } from './rules.model';
import { DEFAULT_RULES } from './rules.constant';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { getObjectId } from '@/shared/utils/commonHelper';
import { UpdateRulesBody } from './rules.interface';
import { Types } from 'mongoose';

const { RulesResponseCodes } = responseCodes;

const rulesMapToObject = (
  rules: Map<string, boolean> | Record<string, boolean> | undefined,
): Record<string, boolean> => {
  if (!rules) return {};
  if (rules instanceof Map) return Object.fromEntries(rules.entries());

  const rawRules = rules as unknown as {
    toObject?: () => Record<string, boolean>;
  };
  if (typeof rawRules.toObject === 'function') return rawRules.toObject();

  return rules as Record<string, boolean>;
};

export const getRules = async (companyId: string | Types.ObjectId, ...ruleKeys: string[]) => {
  const rulesDoc = await Rules.findOne({ companyId: getObjectId(companyId) });

  if (!rulesDoc) {
    if (ruleKeys.length > 0) {
      const result: Record<string, unknown> = {};
      for (const key of ruleKeys) {
        result[key] = DEFAULT_RULES[key as keyof typeof DEFAULT_RULES] ?? null;
      }
      return result;
    }

    return { rules: DEFAULT_RULES };
  }

  const storedRules = rulesMapToObject(rulesDoc.rules as any);
  const mergedRules = {
    ...DEFAULT_RULES,
    ...storedRules,
  };

  if (ruleKeys.length > 0) {
    const result: Record<string, unknown> = {};
    for (const key of ruleKeys) {
      result[key] =
        mergedRules[key as keyof typeof mergedRules] ??
        DEFAULT_RULES[key as keyof typeof DEFAULT_RULES] ??
        null;
    }
    return result;
  }

  const rulesObj = rulesDoc.toObject();
  return { ...rulesObj, rules: mergedRules };
};

export const updateRules = async (
  companyId: string,
  updateData: UpdateRulesBody,
): Promise<boolean> => {
  const existingRulesDoc = await Rules.findOne({ companyId: getObjectId(companyId) });
  const existingRules = existingRulesDoc
    ? rulesMapToObject(existingRulesDoc.rules as any)
    : {};

  const mergedRules = {
    ...DEFAULT_RULES,
    ...existingRules,
    ...(updateData.rules as any),
  };

  const result = await Rules.updateOne(
    { companyId: getObjectId(companyId) },
    { $set: { rules: mergedRules } },
    { upsert: true },
  );

  if (result.matchedCount === 0 && result.upsertedCount === 0)
    throw new ApiError(
      defaultStatus.OK,
      'Failed to update rules',
      true,
      '',
      RulesResponseCodes.RULES_ERROR,
    );

  return true;
};
