import {
  Body,
  Controller,
  Get,
  Inject,
  NotFoundException,
  Param,
  ParseIntPipe,
  Post,
  Put,
  Req,
  UploadedFile,
  UploadedFiles,
  UseInterceptors,
} from '@nestjs/common';
import { Request } from 'express';

import * as fs from 'fs';
import { MerchantService } from './merchant.service';
import {
  FileFieldsInterceptor,
  FileInterceptor,
} from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname, join } from 'path';
import { MerchantdocumentsService } from './merchantdocuments/merchantdocuments.service';
import { ClientProxy, MessagePattern } from '@nestjs/microservices';
import { MerchantHasCategoryService } from './merchant-has-category/merchant-has-category.service';
import { firstValueFrom } from 'rxjs';

@Controller('api/merchant')
export class MerchantController {
  constructor(
    private readonly merchantService: MerchantService,
    private merchantDocumentsService: MerchantdocumentsService,
    private merchantHasCategoriesService: MerchantHasCategoryService,

    @Inject('SHOP_SERVICE') private readonly shopServiceClient: ClientProxy,
  ) {}

  @MessagePattern({ cmd: 'merchant_get_all' })
  async getAll(data: { protocol: string; host: string }): Promise<any> {
    const { protocol, host } = data;
    console.log('The host is :', host);
    const baseUrl = `${protocol}://${host}/uploads/merchant/`;
    const baseUrl2 = `${protocol}://${host}/uploads/merchant/documents/`;

    const merchants = await this.merchantService.getAll();
    const allDocuments = await this.merchantDocumentsService.findAll();
    const merchantsWithDetails = await Promise.all(
      merchants.map(async (merchant) => {
        const logoUrl = merchant.Logo_Path
          ? baseUrl + merchant.Logo_Path
          : null;

        const merchantDocs = allDocuments
          .filter((doc) => doc.Merchant_idMerchant === merchant.idMerchant)
          .map((doc) => ({
            ...doc,
            Path_Link: baseUrl2 + doc.Path,
          }));

        const merchant_Has_categories =
          await this.merchantHasCategoriesService.getMerchantHasCategoriesByMerchantId(
            merchant.idMerchant,
          );

        return {
          ...merchant,
          Logo_Path_Link: logoUrl,
          Documents: merchantDocs,
          merchant_Has_categories,
        };
      }),
    );

    return merchantsWithDetails;
  }

  @MessagePattern({ cmd: 'merchant_get_by_id' })
  async getById(data: {
    id: number;
    protocol: string;
    host: string;
  }): Promise<any> {
    const protocol = data.protocol;
    const host = data.host;
    console.log('The host is :', host);
    const id = data.id;
    const baseLogoUrl = `${protocol}://${host}/uploads/merchant/`;
    const baseDocUrl = `${protocol}://${host}/uploads/merchant/documents/`;

    const merchant = await this.merchantService.getById(id);
    if (!merchant) {
      throw new NotFoundException('Merchant not found');
    }

    const logoUrl = merchant.Logo_Path
      ? baseLogoUrl + merchant.Logo_Path
      : null;

    const documents = await this.merchantDocumentsService.findByMerchantId(id);
    const documentList = documents.map((doc) => ({
      ...doc,
      Path_Link: doc.Path ? baseDocUrl + doc.Path : null,
    }));

    const merchant_Has_categories =
      await this.merchantHasCategoriesService.getMerchantHasCategoriesByMerchantId(
        id,
      );

    const shop_items = await firstValueFrom(
      this.shopServiceClient.send(
        { cmd: 'get_shop_item_by_merchant_id' },
        { Merchant_idMerchant: id },
      ),
    );

    const shop_item_categories = await firstValueFrom(
      this.shopServiceClient.send(
        { cmd: 'shop_item_category_get_by_merchant_id' },
        { Merchant_idMerchant: id },
      ),
    );

    return {
      ...merchant,
      Logo_Path_Link: logoUrl,
      Documents: documentList,
      merchant_Has_categories,
      shop_items,
      shop_item_categories,
    };
  }

  @MessagePattern({ cmd: 'merchant_create' })
  async create(data: {
    body: any;
    files: {
      Logo?: Express.Multer.File[];
      documents?: Express.Multer.File[];
    };
    merchantCategory: any;
  }): Promise<any> {
    const { body, files, merchantCategory } = data;
    console.log('Merchant category in 110 : ', merchantCategory);
    const logo = files.Logo?.[0];
    const documentFiles = files.documents || [];
    const documentsMetadata = JSON.parse(body.documentMetadata || '[]');

    // Create the merchant with the 'Logo'
    const createdMerchant = await this.merchantService.create(body, logo);
    console.log('create it ', createdMerchant);

    if (documentFiles.length > 0) {
      // Process the documents
      await Promise.all(
        documentFiles.map((file, index) => {
          const documentDto = {
            fileName: file.filename,
            description: documentsMetadata[index]?.Description || '',
            merchantId: createdMerchant.idMerchant,
          };
          return this.merchantDocumentsService.addMerchantDocuments(
            documentDto,
          );
        }),
      );
    }

    let parsedMerchantCategory: number[] = [];

    if (typeof merchantCategory === 'string') {
      try {
        parsedMerchantCategory = JSON.parse(merchantCategory);
      } catch (err) {
        console.error(
          'Invalid merchantCategory string received:',
          merchantCategory,
        );
      }
    } else if (Array.isArray(merchantCategory)) {
      parsedMerchantCategory = merchantCategory;
    }
    if (parsedMerchantCategory.length > 0) {
      await Promise.all(
        parsedMerchantCategory.map(async (item) => {
          await this.merchantHasCategoriesService.addMerchantHasCategories(
            createdMerchant.idMerchant,
            item,
          );
        }),
      );
    }

    return {
      merchant: createdMerchant,
      message: 'Merchant and documents created successfully',
    };
  }

  @MessagePattern({ cmd: 'merchant_update' })
  async update(data: {
    id: number;
    body: any;
    files: {
      Logo?: Express.Multer.File[];
      documents?: Express.Multer.File[];
    };
    merchantCategory: any;
  }): Promise<any> {
    const { id, body, files, merchantCategory } = data;
    const logo = files.Logo?.[0];
    const documentFiles = files.documents || [];

    let documentsMetadata: { Description: string }[] = [];
    try {
      documentsMetadata = JSON.parse(body.documentMetadata || '[]');
    } catch (error) {
      console.warn('Invalid JSON in documents:', body.documents);
    }

    let existingDocumentInfo: { Path: string; Description: string }[] = [];
    try {
      existingDocumentInfo = JSON.parse(body.documents_exist || '[]');
    } catch (error) {
      console.warn('Invalid JSON in documents_exist:', body.documents_exist);
    }

    const merchant = await this.merchantService.findById(id);
    console.log('The merchant : ', merchant);
    if (!merchant) throw new NotFoundException('Merchant not found');

    if (logo) {
      body.Logo_Path = logo.filename;
    }
    await this.merchantService.update(id, body);

    const currentDocuments =
      await this.merchantDocumentsService.findByMerchantId(id);

    await Promise.all(
      currentDocuments.map((doc) => {
        const match = existingDocumentInfo.find((d) => d.Path === doc.Path);
        if (match && match.Description !== doc.Description) {
          return this.merchantDocumentsService.updateDescriptionById(
            doc.idMerchant_Document,
            match.Description,
          );
        }
        return Promise.resolve();
      }),
    );

    const existingPaths = existingDocumentInfo.map((d) => d.Path);
    const docsToDelete = currentDocuments.filter(
      (doc) => !existingPaths.includes(doc.Path),
    );

    await Promise.all(
      docsToDelete.map((doc) =>
        this.merchantDocumentsService.deleteById(doc.idMerchant_Document),
      ),
    );

    await Promise.all(
      documentFiles.map((file, index) => {
        const documentDto = {
          fileName: file.filename,
          description: documentsMetadata[index]?.Description || '',
          merchantId: id,
        };
        return this.merchantDocumentsService.addMerchantDocuments(documentDto);
      }),
    );

    let parsedMerchantCategory: number[] = [];
    if (typeof merchantCategory === 'string') {
      try {
        parsedMerchantCategory = JSON.parse(merchantCategory);
      } catch (err) {
        console.error(
          'Invalid merchantCategory string received:',
          merchantCategory,
        );
      }
    } else if (Array.isArray(merchantCategory)) {
      parsedMerchantCategory = merchantCategory;
    }

    //in here we are performing delete existing marchant has categories

    if (parsedMerchantCategory.length > 0) {
      await this.merchantHasCategoriesService.deleteExistingCategoriesById(id);
      //adding new merchant categories now
      await Promise.all(
        parsedMerchantCategory.map(async (item) => {
          await this.merchantHasCategoriesService.addMerchantHasCategories(
            id,
            item,
          );
        }),
      );
    }

    return {
      message: 'Merchant updated successfully',
    };
  }
}
