import { v4 as uuidv4 } from 'uuid';
import { db } from '../application/database';
import { ResponseError } from '../error/response-error';
import { UserPackageOptionValidation } from '../validation/user-package-option-validation';
import { Validation } from '../validation/validation';
import {
  UserPackageOptionResponse,
  toUserPackageOptionResponse,
  BulkUpdateRequest,
  UserPackageOption,
} from '../model/user-package-option-model';

export class UserPackageOptionService {
  static async getAll(): Promise<UserPackageOptionResponse[]> {
    // Fetch all user package options
    const options = await db.query<UserPackageOption & { packageType: { name: string } }>(`
      SELECT
        upo.*,
        JSON_OBJECT('name', pt.name) AS packageType
      FROM
        user_package_option upo
      JOIN
        package_type pt ON upo.package_type_id = pt.id
      ORDER BY
        upo.order_number ASC
    `);

    // Parse packageType JSON object
    options.forEach((option) => {
      if (typeof option.packageType === 'string') {
        option.packageType = JSON.parse(option.packageType);
      }
    });

    if (!options) {
      throw new ResponseError(404, 'User package options tidak ditemukan');
    }

    return options.map(toUserPackageOptionResponse);
  }

  static async getByType(type: string): Promise<UserPackageOptionResponse[]> {
    // Format package type name
    type = type.replace(/-/g, ' ');

    // Fetch package type ID
    const packageType = await db.queryOne<{ id: string }>(`SELECT id FROM package_type WHERE name = ?`, [type]);

    if (!packageType) {
      throw new ResponseError(404, 'Package type tidak ditemukan');
    }

    // Fetch user package options by package type ID
    const options = await db.query<UserPackageOption & { packageType: { name: string } }>(
      `
      SELECT
        upo.*,
        JSON_OBJECT('name', pt.name) AS packageType
      FROM
        user_package_option upo
      JOIN
        package_type pt ON upo.package_type_id = pt.id
      WHERE
        upo.package_type_id = ?
      ORDER BY
        upo.order_number ASC
    `,
      [packageType.id]
    );

    // Parse packageType JSON object
    options.forEach((option) => {
      if (typeof option.packageType === 'string') {
        option.packageType = JSON.parse(option.packageType);
      }
    });

    if (!options) {
      throw new ResponseError(404, 'User package options tidak ditemukan');
    }

    return options.map(toUserPackageOptionResponse);
  }

  static async bulkUpdate(request: BulkUpdateRequest): Promise<void> {
    // Validate the bulk update request
    const { type, modifiedData, deletedData } = request;
    const validatedModifiedData = modifiedData?.map((service) =>
      Validation.validate(UserPackageOptionValidation.BULK_UPDATE, service)
    );

    // Fetch package type ID
    const serviceType = await db.queryOne<{ id: string }>(`SELECT id FROM package_type WHERE name = ?`, [
      type.replace(/-/g, ' '),
    ]);

    if (!serviceType) {
      throw new ResponseError(404, 'Service type not found');
    }

    // handle update and create
    if (validatedModifiedData.length > 0) {
      await db.transaction(async (conn) => {
        await Promise.all(
          validatedModifiedData.map(async (service) => {
            if (service.id) {
              await conn.query('UPDATE user_package_option SET name = ?, price = ?, order_number = ? WHERE id = ?', [
                service.name,
                service.price,
                service.order_number,
                service.id,
              ]);
            } else {
              await conn.query(
                'INSERT INTO user_package_option (id, name, price, package_type_id, order_number) VALUES (?, ?, ?, ?, ?)',
                [uuidv4(), service.name || '', service.price || 0, serviceType.id, service.order_number]
              );
            }
          })
        );
      });
    }

    // handle delete
    if (deletedData.length > 0) {
      await db.transaction(async (conn) => {
        await conn.query('DELETE FROM user_package_option WHERE id IN (?)', [deletedData]);
      });
    }
  }
}
