const { z } = require('zod');

const toNumber = (val) => {
  if (val === '' || val === null || val === undefined) return undefined;
  if (typeof val === 'string' && val.trim() === '') return undefined;
  const num = Number(val);
  return Number.isNaN(num) ? val : num;
};

const toBoolean = (val) => {
  if (val === true || val === false) return val;
  if (val === 'true') return true;
  if (val === 'false') return false;
  return val;
};

const toOptionalString = (val) => {
  if (val === '' || val === null || val === undefined) return undefined;
  return val;
};

const parseJson = (val) => {
  if (typeof val !== 'string') return val;
  try {
    return JSON.parse(val);
  } catch (_) {
    return val;
  }
};

const zNumber = z.preprocess(toNumber, z.number());
const zNumberOptional = z.preprocess(toNumber, z.number().optional());
const zNumberPositive = z.preprocess(toNumber, z.number().positive());
const zNumberPositiveOptional = z.preprocess(toNumber, z.number().positive().optional());
const zIntPositive = z.preprocess(toNumber, z.number().int().positive());
const zIntPositiveOptional = z.preprocess(toNumber, z.number().int().positive().optional());
const zBoolOptional = z.preprocess(toBoolean, z.boolean().optional());
const zInt01Optional = z.preprocess(
  (val) => {
    if (val === true || val === false) return val ? 1 : 0;
    if (val === 'true' || val === 'false') return val === 'true' ? 1 : 0;
    return toNumber(val);
  },
  z.number().int().min(0).max(1).optional()
);

const zStringOptional = z.preprocess(toOptionalString, z.string().optional());
const zStringOptionalMin1 = z.preprocess(toOptionalString, z.string().min(1).optional());
const zStringOptionalEmail = z.preprocess(toOptionalString, z.string().email().optional());

const emptyBody = z.object({}).passthrough();

const permissionsSchema = z.preprocess(
  parseJson,
  z.union([z.array(z.string()), z.record(z.any()), z.string()]).optional()
);

const authSchemas = {
  login: z.object({
    username: z.string().min(1),
    password: z.string().min(1),
  }),
  verifyTotp: z.object({
    mfa_token: z.string().min(1),
    totp_code: z.string().regex(/^\d{6}$/),
  }),
  verifyPin: z.object({
    pin: z.string().min(1),
  }),
  register: z.object({
    username: z.string().min(1),
    password: z.string().min(1),
    full_name: z.string().min(1),
    role: z.preprocess(toOptionalString, z.enum(['admin', 'manager', 'staff']).optional()),
    pin: zStringOptionalMin1,
    permissions: permissionsSchema,
  }),
};

const userSchemas = {
  updateProfile: z.object({
    full_name: zStringOptionalMin1,
  }),
  changePassword: z.object({
    current_password: z.string().min(1),
    new_password: z.string().min(1),
  }),
  updateUser: z.object({
    full_name: zStringOptionalMin1,
    role: z.preprocess(toOptionalString, z.enum(['admin', 'manager', 'staff']).optional()),
    is_active: zInt01Optional,
    permissions: permissionsSchema,
  }),
  resetPassword: z.object({
    password: z.string().min(1),
  }),
  updatePin: z.object({
    pin: z.string().min(1),
  }),
  verifyMfaSetup: z.object({
    totp_code: z.string().regex(/^\d{6}$/),
  }),
};

const categorySchemas = {
  create: z.object({
    name: z.string().min(1),
  }),
  update: z.object({
    name: zStringOptionalMin1,
  }),
};

const productSchemas = {
  create: z.object({
    name: z.string().min(1),
    sku: zStringOptional,
    variant: zStringOptional,
    category_id: zIntPositive,
    selling_price: zNumberOptional,
    cost_price: zNumberOptional,
    stock_level: zNumberOptional,
    low_stock_threshold: zNumberOptional,
    status: zStringOptional,
  }),
  update: z.object({
    name: zStringOptionalMin1,
    sku: zStringOptionalMin1,
    variant: zStringOptional,
    category_id: zIntPositiveOptional,
    selling_price: zNumberOptional,
    cost_price: zNumberOptional,
    stock_level: zNumberOptional,
    low_stock_threshold: zNumberOptional,
    status: zStringOptional,
  }),
  adjustStock: z.object({
    quantity_change: zNumber,
    reason: z.string().min(1),
  }),
};

const customerSchemas = {
  create: z.object({
    first_name: z.string().min(1),
    last_name: z.string().min(1),
    phone: zStringOptional,
    email: zStringOptionalEmail,
    customer_type: zStringOptional,
    risk_level: zStringOptional,
    balance_status: zStringOptional,
    notes: zStringOptional,
    ledger_balance: zNumberOptional,
  }),
  update: z.object({
    first_name: zStringOptionalMin1,
    last_name: zStringOptionalMin1,
    phone: zStringOptional,
    email: zStringOptionalEmail,
    customer_type: zStringOptional,
    risk_level: zStringOptional,
    balance_status: zStringOptional,
    notes: zStringOptional,
    ledger_balance: zNumberOptional,
  }),
  addPayment: z.object({
    amount: zNumberPositive,
    payment_method: zStringOptional,
    transaction_id: zIntPositiveOptional,
    notes: zStringOptional,
  }),
  addDue: z.object({
    amount: zNumberPositive,
    category: zStringOptional,
    due_date: zStringOptional,
    description: zStringOptional,
  }),
};

const dailyEntrySchemas = {
  create: z.object({
    entry_date: z.string().min(1),
    type: z.string().min(1),
    rmt: zNumberOptional,
    bkash: zNumberOptional,
    product: zNumberOptional,
    bill: zNumberOptional,
    ticket: zNumberOptional,
    others: zNumberOptional,
    notes: zStringOptional,
    receipt_url: zStringOptional,
    fields_data: z.preprocess(parseJson, z.record(z.any()).optional()),
  }),
  update: z.object({
    entry_date: zStringOptionalMin1,
    type: zStringOptionalMin1,
    rmt: zNumberOptional,
    bkash: zNumberOptional,
    product: zNumberOptional,
    bill: zNumberOptional,
    ticket: zNumberOptional,
    others: zNumberOptional,
    notes: zStringOptional,
    receipt_url: zStringOptional,
    fields_data: z.preprocess(parseJson, z.record(z.any()).optional()),
  }),
};

const expenseEntrySchemas = {
  create: z.object({
    entry_date: z.string().min(1),
    category: z.string().min(1),
    amount: zNumber,
    description: zStringOptional,
    receipt_url: zStringOptional,
  }),
  update: z.object({
    entry_date: zStringOptionalMin1,
    category: zStringOptionalMin1,
    amount: zNumberOptional,
    description: zStringOptional,
    receipt_url: zStringOptional,
  }),
};

const airlineSchemas = {
  create: z.object({
    name: z.string().min(1),
    code: z.string().min(1),
  }),
  update: z.object({
    name: zStringOptionalMin1,
    code: zStringOptionalMin1,
  }),
};

const ticketSchemas = {
  create: z.object({
    pnr_code: z.string().min(1),
    passenger_name: z.string().min(1),
    passenger_phone: zStringOptional,
    airline_id: zIntPositive,
    route: zStringOptional,
    travel_date: zStringOptional,
    net_cost: zNumber,
    selling_price: zNumber,
    seat_fee: zNumberOptional,
    extra_luggage_qty: zNumberOptional,
    extra_luggage_cost: zNumberOptional,
    visa_required: zBoolOptional,
    payment_method: zStringOptional,
    customer_id: zIntPositiveOptional,
    notes: zStringOptional,
    paid_amount: zNumberOptional,
  }),
  update: z.object({
    status: zStringOptional,
    route: zStringOptional,
    extra_luggage_qty: zNumberOptional,
    extra_luggage_cost: zNumberOptional,
  }),
};

const transactionItemSchema = z.object({
  product_id: zIntPositiveOptional.nullable(),
  quantity: zNumberPositive,
  item_name: zStringOptionalMin1,
  name: zStringOptionalMin1,
  unit_price: zNumberPositiveOptional,
  selling_price: zNumberPositiveOptional,
  cost_price: zNumberOptional,
}).superRefine((val, ctx) => {
  if (val.product_id == null) {
    if (!val.item_name && !val.name) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: 'Manual items require item_name or name',
        path: ['item_name'],
      });
    }
    if (val.unit_price == null && val.selling_price == null) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: 'Manual items require unit_price or selling_price',
        path: ['unit_price'],
      });
    }
  }
});

const transactionSchemas = {
  create: z.object({
    payment_method: zStringOptional,
    customer_id: zIntPositiveOptional,
    vat_percent: zNumberOptional,
    items: z.array(transactionItemSchema).min(1),
    notes: zStringOptional,
    paid_amount: zNumberOptional,
    discount_type: z.enum(['percentage', 'fixed_amount']).optional(),
    discount_value: zNumberOptional,
  }),
};

const settingsSchemas = {
  upsert: z.object({
    key: z.string().min(1),
    value: z.any().optional(),
  }),
  bulkUpsert: z.object({
    settings: z.record(z.any()),
  }),
};

const timesheetSchemas = {
  pin: z.object({
    pin: z.string().min(1),
  }),
  updateEntry: z.object({
    attendance_date: zStringOptionalMin1,
    clock_in_at: zStringOptionalMin1,
    clock_out_at: zStringOptionalMin1,
    notes: zStringOptional,
  }).refine(
    (data) => Object.values(data).some((value) => value !== undefined),
    { message: 'At least one field is required' }
  ),
  empty: emptyBody,
};

const sessionSchemas = {
  forceEnd: z.object({
    session_id: zIntPositive,
  }),
};

const paramsSchemas = {
  idParam: z.object({
    id: zIntPositive,
  }),
  keyParam: z.object({
    key: z.string().min(1),
  }),
};

module.exports = {
  authSchemas,
  userSchemas,
  categorySchemas,
  productSchemas,
  customerSchemas,
  dailyEntrySchemas,
  expenseEntrySchemas,
  airlineSchemas,
  ticketSchemas,
  transactionSchemas,
  settingsSchemas,
  timesheetSchemas,
  sessionSchemas,
  paramsSchemas,
};
