/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-shadow */
import {inject} from '@loopback/core';
import {
  Count,
  CountSchema,
  Filter,
  FilterExcludingWhere,
  repository,
  Where,
} from '@loopback/repository';
import {
  del,
  get,
  getModelSchemaRef,
  param,
  patch,
  post,
  put,
  requestBody,
  Request,
  response,
  RestBindings
} from '@loopback/rest';
import fs from 'fs';
import multer from 'multer';
import path from 'path';
import {URL} from '../config/constants';
import {PackageMaster} from '../models';
import {PackageMasterRepository} from '../repositories';

const BASE_URL = URL;

// --------- Configure Upload Directory ----------
const uploadDir = path.join(__dirname, '../../uploads/packages');
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, {recursive: true});
}

const storage = multer.diskStorage({
  destination: function (_req, _file, cb) {
    cb(null, uploadDir);
  },
  filename: function (_req, file, cb) {
    // Generate a unique filename with original extension
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    const ext = path.extname(file.originalname);
    cb(null, `package-${uniqueSuffix}${ext}`);
  },
});

const upload = multer({storage});

export class PackageMasterController {
  constructor(
    @repository(PackageMasterRepository)
    public packageMasterRepository: PackageMasterRepository,
  ) { }

  // ---------- CREATE without image ----------
  @post('/package-masters/json')
  @response(200, {
    description: 'PackageMaster model instance',
    content: {'application/json': {schema: getModelSchemaRef(PackageMaster)}},
  })
  async create(
    @requestBody({
      content: {
        'application/json': {
          schema: getModelSchemaRef(PackageMaster, {
            title: 'NewPackageMaster',
            exclude: ['packageId'],
          }),
        },
      },
    })
    packageMaster: Omit<PackageMaster, 'packageId'>,
  ): Promise<PackageMaster> {
    return this.packageMasterRepository.create(packageMaster);
  }

  // ---------- CREATE with image ----------
  @post('/package-masters')
  @response(200, {
    description: 'Create package with image',
    content: {'application/json': {schema: getModelSchemaRef(PackageMaster)}},
  })
  async createWithFile(
    @requestBody.file()
    request: Request,
    @inject(RestBindings.Http.RESPONSE) response: any,
  ): Promise<PackageMaster> {
    return new Promise<PackageMaster>((resolve, reject) => {
      // eslint-disable-next-line @typescript-eslint/no-misused-promises
      upload.single('packagePhoto')(request, response, async (err: any) => {
        if (err) return reject(err);

        const file = (request as any).file;
        const fields = (request as any).body;

        try {
          const newPackage = await this.packageMasterRepository.create({
            fkusertypeId: parseInt(fields.fkusertypeId),
            packageName: fields.packageName,
            packageDescription: fields.packageDescription,
            packageduration: fields.packageduration ? parseInt(fields.packageduration) : undefined,
            packageCost: fields.packageCost ? parseFloat(fields.packageCost) : undefined,
            propertyVisit: fields.propertyVisit ? parseInt(fields.propertyVisit) : undefined,
            propertyAddToCart: fields.propertyAddToCart ? parseInt(fields.propertyAddToCart) : undefined,
            isDefault: fields.isDefault ? parseInt(fields.isDefault) : 1,
            isOn: fields.isOn ? parseInt(fields.isOn) : 1,
            isActive: fields.isActive ? parseInt(fields.isActive) : 1,
            packagePhoto: file ? `${BASE_URL}/uploads/packages/${file.filename}` : undefined,
            createdDate: new Date().toISOString(),
            createdBy: fields.createdBy ? parseInt(fields.createdBy) : undefined,
            lastChanged: new Date().toISOString(),
          });
          resolve(newPackage);
        } catch (e) {
          reject(e);
        }
      });
    });
  }

  // ---------- COUNT ----------
  @get('/package-masters/count')
  @response(200, {
    description: 'PackageMaster model count',
    content: {'application/json': {schema: CountSchema}},
  })
  async count(
    @param.where(PackageMaster) where?: Where<PackageMaster>,
  ): Promise<Count> {
    return this.packageMasterRepository.count(where);
  }

  // ---------- FIND ----------
  @get('/package-masters')
  @response(200, {
    description: 'Array of PackageMaster model instances',
    content: {
      'application/json': {
        schema: {
          type: 'array',
          items: getModelSchemaRef(PackageMaster, {includeRelations: true}),
        },
      },
    },
  })
  async find(
    @param.filter(PackageMaster) filter?: Filter<PackageMaster>,
  ): Promise<PackageMaster[]> {
    return this.packageMasterRepository.find({
      ...filter,
      include: [{
        relation: 'usertype',
        scope: {
          fields: {
            userId: true,
            username: true,
          },
        },
      }],
    });
  }

  // ---------- FIND BY ID ----------
  @get('/package-masters/{id}')
  @response(200, {
    description: 'PackageMaster model instance',
    content: {
      'application/json': {
        schema: getModelSchemaRef(PackageMaster, {includeRelations: true}),
      },
    },
  })
  async findById(
    @param.path.number('id') id: number,
    @param.filter(PackageMaster, {exclude: 'where'}) filter?: FilterExcludingWhere<PackageMaster>
  ): Promise<PackageMaster> {
    return this.packageMasterRepository.findById(id, {
      ...filter,
      include: [{
        relation: 'usertype',
        scope: {
          fields: {
            userId: true,
            username: true,
          },
        },
      }],
    });
  }

  // ---------- Standard UPDATE (without image) - JSON only ----------
  @patch('/package-masters/{id}')
  @response(204, {
    description: 'PackageMaster PATCH success (JSON only)',
  })
  async updateById(
    @param.path.number('id') id: number,
    @requestBody({
      content: {
        'application/json': {
          schema: getModelSchemaRef(PackageMaster, {partial: true}),
        },
      },
    })
    packageMaster: PackageMaster,
  ): Promise<void> {
    await this.packageMasterRepository.updateById(id, {
      ...packageMaster,
      lastChanged: new Date().toISOString(),
    });
  }

  // ---------- UPDATE with image (multipart/form-data) ----------
  @patch('/package-masters/with-image/{id}')
  @response(204, {
    description: 'PackageMaster PATCH success with image upload',
  })
  async updateByIdWithFile(
    @param.path.number('id') id: number,
    @requestBody.file()
    request: Request,
    @inject(RestBindings.Http.RESPONSE) response: any,
  ): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      // eslint-disable-next-line @typescript-eslint/no-misused-promises
      upload.single('packagePhoto')(request, response, async (err: any) => {
        if (err) return reject(err);

        const file = (request as any).file;
        const fields = (request as any).body;

        try {
          // Get existing record
          const existingPackage = await this.packageMasterRepository.findById(id);

          // If there's an old photo and a new one is uploaded, delete the old one
          if (file && existingPackage.packagePhoto) {
            const oldPhotoPath = existingPackage.packagePhoto.replace(`${BASE_URL}/uploads/packages/`, '');
            const fullOldPath = path.join(uploadDir, oldPhotoPath);
            if (fs.existsSync(fullOldPath)) {
              fs.unlinkSync(fullOldPath);
            }
          }

          // Determine the photo value
          let photoValue: string | undefined;
          if (file) {
            // New file uploaded
            photoValue = `${BASE_URL}/uploads/packages/${file.filename}`;
          } else if (fields.packagePhoto === 'null' || fields.packagePhoto === '' || fields.packagePhoto === null) {
            // Explicitly delete the photo
            photoValue = undefined;
            // Delete the existing file if any
            if (existingPackage.packagePhoto) {
              const oldPhotoPath = existingPackage.packagePhoto.replace(`${BASE_URL}/uploads/packages/`, '');
              const fullOldPath = path.join(uploadDir, oldPhotoPath);
              if (fs.existsSync(fullOldPath)) {
                fs.unlinkSync(fullOldPath);
              }
            }
          } else {
            // Keep existing photo
            photoValue = existingPackage.packagePhoto;
          }

          await this.packageMasterRepository.updateById(id, {
            fkusertypeId: fields.fkusertypeId ? parseInt(fields.fkusertypeId) : existingPackage.fkusertypeId,
            packageName: fields.packageName || existingPackage.packageName,
            packageDescription: fields.packageDescription || existingPackage.packageDescription,
            packageduration: fields.packageduration ? parseInt(fields.packageduration) : existingPackage.packageduration,
            packageCost: fields.packageCost ? parseFloat(fields.packageCost) : existingPackage.packageCost,
            propertyVisit: fields.propertyVisit ? parseInt(fields.propertyVisit) : existingPackage.propertyVisit,
            propertyAddToCart: fields.propertyAddToCart ? parseInt(fields.propertyAddToCart) : existingPackage.propertyAddToCart,
            isDefault: fields.isDefault ? parseInt(fields.isDefault) : existingPackage.isDefault,
            isOn: fields.isOn ? parseInt(fields.isOn) : existingPackage.isOn,
            isActive: fields.isActive ? parseInt(fields.isActive) : existingPackage.isActive,
            packagePhoto: photoValue,
            modifiedDate: new Date().toISOString(),
            modifiedBy: fields.modifiedBy ? parseInt(fields.modifiedBy) : existingPackage.modifiedBy,
            lastChanged: new Date().toISOString(),
          });

          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
  }

  // ---------- REPLACE ----------
  @put('/package-masters/{id}')
  @response(204, {
    description: 'PackageMaster PUT success',
  })
  async replaceById(
    @param.path.number('id') id: number,
    @requestBody() packageMaster: PackageMaster,
  ): Promise<void> {
    await this.packageMasterRepository.replaceById(id, {
      ...packageMaster,
      lastChanged: new Date().toISOString(),
    });
  }

  // ---------- DELETE ----------
  @del('/package-masters/{id}')
  @response(204, {
    description: 'PackageMaster DELETE success',
  })
  async deleteById(@param.path.number('id') id: number): Promise<void> {
    // Get the package to delete the associated photo
    const packageToDelete = await this.packageMasterRepository.findById(id);

    // Delete the photo file if it exists
    if (packageToDelete.packagePhoto) {
      const photoPath = packageToDelete.packagePhoto.replace(`${BASE_URL}/uploads/packages/`, '');
      const fullPath = path.join(uploadDir, photoPath);
      if (fs.existsSync(fullPath)) {
        fs.unlinkSync(fullPath);
      }
    }

    // Delete the record from the database
    await this.packageMasterRepository.deleteById(id);
  }

  // ---------- SOFT DELETE ----------
  @patch('/package-masters/soft-delete/{id}')
  @response(204, {
    description: 'PackageMaster soft delete success',
  })
  async softDeletePackage(
    @param.path.number('id') id: number,
    @requestBody() deleteData?: {deletedBy?: number, deleteRemark?: string},
  ): Promise<void> {
    await this.packageMasterRepository.updateById(id, {
      isActive: 0,
      deletedDate: new Date().toISOString(),
      deletedBy: deleteData?.deletedBy,
      deleteRemark: deleteData?.deleteRemark,
      lastChanged: new Date().toISOString(),
    });
  }
}
