import {
  BadRequestException,
  HttpStatus,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BidMetadata } from '../entities/bid-metadata.entity';
import { Bid } from '../entities/bid.entity';
import AppResponse from '@/response/app-response';
import { plainToClass } from 'class-transformer';

@Injectable()
export class BidMetadatasService {
  constructor(
    @InjectRepository(BidMetadata)
    readonly bidMetadataRepo: Repository<BidMetadata>,
  ) {}

  async upsert(data: Record<string, any>, bid: Bid) {
    const existingMetadata = await this.bidMetadataRepo.find({
      where: { bid: { id: bid.id } },
    });

    const existingMap = new Map(
      existingMetadata.map((item) => [item.key_name, item]),
    );

    const toSave: BidMetadata[] = [];

    for (const [key, value] of Object.entries(data)) {
      const existing = existingMap.get(key);
      if (existing) {
        existing.value = value;
        toSave.push(existing);
      } else {
        toSave.push(
          this.bidMetadataRepo.create({
            key_name: key,
            value,
            bid: { id: bid.id },
          }),
        );
      }
    }

    await this.bidMetadataRepo.save(toSave);
    return true;
  }

  async update(id: BidMetadata['id'], value: string) {
    const result = await this.bidMetadataRepo.update(id, { value: value });

    if (!result) throw new BadRequestException(AppResponse.toResponse(null));

    const data = await this.bidMetadataRepo.findOne({ where: { id } });

    return AppResponse.toResponse(plainToClass(BidMetadata, data));
  }

  getMode = (metadatas: BidMetadata[]) => {
    const result = metadatas.find(
      (item) => item.key_name === BidMetadata.MODE_KEY,
    );

    return result ? result.value : null;
  };

  getDemoResponse = (metadatas: BidMetadata[]) => {
    const result = metadatas.find(
      (item) => item.key_name === BidMetadata.DEMO_RESPONSE,
    );

    return result ? result.value : null;
  };

  getLatestRecordLink = (metadatas: BidMetadata[]) => {
    const result = metadatas.find(
      (item) => item.key_name === BidMetadata.LATEST_RECROD_LINK,
    );

    return result ? result.value : null;
  };

  getArrivalOffsetSecondsSandbox = (metadatas: BidMetadata[]) => {
    const result = metadatas.find(
      (item) => item.key_name === BidMetadata.ARRIAVAL_OFFSET_SECONDS_SANDBOX,
    );

    return result ? Number(result.value) : null;
  };

  getArrivalOffsetSecondsLive = (metadatas: BidMetadata[]) => {
    const result = metadatas.find(
      (item) => item.key_name === BidMetadata.ARRIAVAL_OFFSET_SECONDS_LIVE,
    );

    return result ? Number(result.value) : null;
  };

  getArrivalOffsetSecondsByMode = (metadatas: BidMetadata[]) => {
    const mode = this.getMode(metadatas) || 'live';

    if (mode === 'live') {
      return this.getArrivalOffsetSecondsLive(metadatas);
    }

    return this.getArrivalOffsetSecondsSandbox(metadatas);
  };

  isSandbox(metadatas: BidMetadata[]) {
    return this.getMode(metadatas) !== 'live';
  }

  async delete(id: Bid['id']) {
    const bid = await this.bidMetadataRepo.findOne({ where: { id } });

    if (!bid)
      throw new NotFoundException(
        AppResponse.toResponse(false, {
          message: 'Meta is not found',
          status_code: HttpStatus.NOT_FOUND,
        }),
      );

    await this.bidMetadataRepo.delete({ id: bid.id });

    return AppResponse.toResponse(true, { message: 'Delete success !' });
  }
}
