import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { Logger } from 'pino-nestjs';
import { createCanvas, loadImage, Image } from 'canvas';
import sharp from 'sharp';

export type MissingFlyerInput = {
  name: string;
  breed: string;
  color: string;
  size: string;
  pet_type: string;
  lastSeen: string;
  location: string;
  description: string;
  ownerName: string;
  phone: string;
  petImage: string;
  qrImage: string;
};

export type FoundFlyerInput = {
  breed: string;
  color: string;
  size: string;
  pet_type: string;
  lastSeen: string;
  location: string;
  description: string;
  ownerName: string;
  phone: string;
  petImage: string;
  qrImage: string;
};

const SPACING = {
  xs: 8,
  sm: 12,
  md: 18,
  lg: 24,
};

@Injectable()
export class FlyerService {
  constructor(private readonly logger: Logger) {}

  private getWrappedLines(ctx: any, text: string, maxWidth: number): number {
    const words = text.split(' ');
    let line = '';
    let lines = 1;

    for (let i = 0; i < words.length; i++) {
      const test = line + words[i] + ' ';

      if (ctx.measureText(test).width > maxWidth && i > 0) {
        lines++;
        line = words[i] + ' ';
      } else {
        line = test;
      }
    }

    return lines;
  }

  private wrapText(
    color: string,
    ctx: any,
    text: string,
    x: number,
    y: number,
    maxWidth: number,
    lineHeight: number,
  ): void {
    const words = text.split(' ');
    let line = '';
    let cy = y;

    ctx.fillStyle = color;

    for (let i = 0; i < words.length; i++) {
      const test = line + words[i] + ' ';

      if (ctx.measureText(test).width > maxWidth && i > 0) {
        ctx.fillText(line, x, cy);
        line = words[i] + ' ';
        cy += lineHeight;
      } else {
        line = test;
      }
    }

    ctx.fillText(line, x, cy);
  }

  private drawImageCover(ctx: any, img: any, x: number, y: number, w: number, h: number): void {
    const imgRatio = img.width / img.height;
    const boxRatio = w / h;

    let sx = 0,
      sy = 0,
      sw = img.width,
      sh = img.height;

    if (imgRatio > boxRatio) {
      sw = img.height * boxRatio;
      sx = (img.width - sw) / 2;
    } else {
      sh = img.width / boxRatio;
      sy = (img.height - sh) / 2;
    }

    ctx.drawImage(img, sx, sy, sw, sh, x, y, w, h);
  }

  private roundRect(ctx: any, x: number, y: number, w: number, h: number, r: number): void {
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.arcTo(x + w, y, x + w, y + h, r);
    ctx.arcTo(x + w, y + h, x, y + h, r);
    ctx.arcTo(x, y + h, x, y, r);
    ctx.arcTo(x, y, x + w, y, r);
    ctx.closePath();
  }

  private async loadSmartImage(source: string): Promise<Image> {
    this.logger.log(`Loading image for flyer: ${source}`);
    if (source.startsWith('http://') || source.startsWith('https://')) {
      try {
        const res = await fetch(source);
        if (!res.ok) throw new Error(`Failed to fetch image: ${res.statusText}`);
        const arrayBuffer = await res.arrayBuffer();

        // Convert all remote images (like AVIF/HEIC/etc) to PNG so canvas can read them
        const safeBuffer = await sharp(Buffer.from(arrayBuffer)).png().toBuffer();

        return await loadImage(safeBuffer);
      } catch (error: any) {
        this.logger.error(`Error loading remote image: ${source}`, error);
        throw error;
      }
    }

    try {
      return await loadImage(source);
    } catch (error: any) {
      this.logger.error(`Error loading local image: ${source}`, error);
      throw error;
    }
  }

  private getMissingHeight(data: MissingFlyerInput): number {
    const W = 900;
    const canvas = createCanvas(W, 10);
    const ctx = canvas.getContext('2d');

    const imageH = 300;
    const rightWidth = W - 470 - 80;
    const descWidth = W - 160;

    ctx.font = 'bold 22px Arial';
    const labelH = 22;
    ctx.font = '26px Arial';
    const valueLineH = 30;
    const gap = 15;

    const nameH = labelH + valueLineH + gap;

    const breedLines = this.getWrappedLines(ctx, data.breed, rightWidth);
    const breedH = labelH + breedLines * valueLineH + gap;

    const colorH = labelH + valueLineH + gap;
    const sizeH = labelH + valueLineH + gap;
    const lastSeenH = labelH + valueLineH + gap;

    const locationLines = this.getWrappedLines(ctx, data.location, rightWidth);
    const locationH = labelH + locationLines * valueLineH;

    const totalRightH = 20 + nameH + breedH + colorH + sizeH + lastSeenH + locationH;
    const topSectionH = Math.max(imageH, totalRightH);

    ctx.font = '22px Arial';
    const descLines = this.getWrappedLines(ctx, data.description, descWidth - 40);
    const descH = 30 + descLines * 22 + 20;

    return (
      120 + // header
      80 + // top padding
      topSectionH +
      SPACING.lg +
      descH +
      SPACING.lg +
      165 + // contact box
      80 // bottom padding
    );
  }

  private getFoundHeight(data: FoundFlyerInput): number {
    const W = 900;
    const canvas = createCanvas(W, 10);
    const ctx = canvas.getContext('2d');

    const imageH = 300;
    const rightWidth = W - 470 - 80;
    const descWidth = W - 160;

    ctx.font = 'bold 22px Arial';
    const labelH = 22;
    ctx.font = '26px Arial';
    const valueLineH = 30;
    const gap = 15;

    const breedLines = this.getWrappedLines(ctx, data.breed, rightWidth);
    const breedH = labelH + breedLines * valueLineH + gap;

    const colorH = labelH + valueLineH + gap;
    const sizeH = labelH + valueLineH + gap;
    const lastSeenH = labelH + valueLineH + gap;

    const locationLines = this.getWrappedLines(ctx, data.location, rightWidth);
    const locationH = labelH + locationLines * valueLineH;

    const totalRightH = 20 + breedH + colorH + sizeH + lastSeenH + locationH;
    const topSectionH = Math.max(imageH, totalRightH);

    ctx.font = '16px Arial';
    const descLines = this.getWrappedLines(ctx, data.description, descWidth - 40);
    const descH = 30 + descLines * 22 + 20;

    return (
      120 + // header
      80 + // top padding
      topSectionH +
      SPACING.lg +
      descH +
      SPACING.lg +
      165 + // contact box
      80 // bottom padding
    );
  }

  async generateMissingFlyer(data: MissingFlyerInput): Promise<Buffer> {
    this.logger.log(`Generating missing flyer for pet: ${data.name}`);
    try {
      const W = 900;
      const H = this.getMissingHeight(data);

      const canvas = createCanvas(W, H);
      const ctx = canvas.getContext('2d');

      // 1. BACKGROUND
      ctx.fillStyle = '#f5f5f5';
      ctx.fillRect(0, 0, W, H);

      this.roundRect(ctx, 40, 40, W - 80, H - 80, 20);
      ctx.fillStyle = '#fff';
      ctx.fill();

      // 2. HEADER
      const headerH = 110;
      const gradient = ctx.createLinearGradient(60, 60, W - 60, 170);
      gradient.addColorStop(0, '#E63946');
      gradient.addColorStop(1, '#D62828');

      this.roundRect(ctx, 60, 60, W - 120, headerH, 15);
      ctx.fillStyle = gradient;
      ctx.fill();

      ctx.fillStyle = '#fff';
      ctx.font = 'bold 44px Arial';
      ctx.textAlign = 'center';
      ctx.fillText(`Missing ${data.pet_type[0].toUpperCase() + data.pet_type.slice(1)} !`, W / 2, 130);

      // 3. LAYOUT FLOW
      let y = 200;
      const leftX = 80;
      const rightX = 470;

      // 4. IMAGE
      const petImg = await this.loadSmartImage(data.petImage);
      const imgW = 350;
      const imgH = 300;

      this.roundRect(ctx, leftX, y, imgW, imgH, 18);
      ctx.save();
      ctx.clip();
      this.drawImageCover(ctx, petImg, leftX, y, imgW, imgH);
      ctx.restore();

      // 5. DETAILS
      ctx.textAlign = 'left';

      const label = (t: string, x: number, py: number) => {
        ctx.fillStyle = '#E63946';
        ctx.font = 'bold 22px Arial';
        ctx.fillText(t, x, py);
      };

      const value = (t: string, x: number, py: number) => {
        ctx.fillStyle = '#111';
        ctx.font = `26px Arial`;
        ctx.fillText(t, x, py);
      };

      const rightWidth = W - rightX - 80;
      let ry = y + 20;
      const detailGap = 15;
      const labelH = 22;
      const valueLineH = 30;

      label('Name', rightX, ry);
      value(data.name, rightX, ry + 28);
      ry += labelH + valueLineH + detailGap;

      label('Breed', rightX, ry);
      ctx.font = '26px Arial';
      this.wrapText('#111', ctx, data.breed, rightX, ry + 28, rightWidth, valueLineH);
      ry += labelH + this.getWrappedLines(ctx, data.breed, rightWidth) * valueLineH + detailGap;

      label('Colour', rightX, ry);
      value(data.color, rightX, ry + 28);
      ry += labelH + valueLineH + detailGap;

      label('Size', rightX, ry);
      value(data.size, rightX, ry + 28);
      ry += labelH + valueLineH + detailGap;

      label('Last Seen', rightX, ry);
      value(data.lastSeen, rightX, ry + 28);
      ry += labelH + valueLineH + detailGap;

      label('Last Seen Location', rightX, ry);
      ctx.font = '26px Arial';
      this.wrapText('#111', ctx, data.location, rightX, ry + 28, rightWidth, valueLineH);
      ry += labelH + this.getWrappedLines(ctx, data.location, rightWidth) * valueLineH;

      y = Math.max(y + imgH, ry) + SPACING.lg;

      // 6. DESCRIPTION
      const descX = 80;
      const descW = W - 160;
      const lineH = 22;

      ctx.font = '22px Arial';
      const descLines = this.getWrappedLines(ctx, data.description, descW - 40);
      const descH = 30 + descLines * lineH;

      this.roundRect(ctx, descX, y, descW, descH, 12);
      ctx.fillStyle = '#f9f9f9';
      ctx.fill();

      this.wrapText('#333', ctx, data.description, descX + 20, y + 28, descW - 40, lineH);

      y += descH + SPACING.lg;

      // 7. CONTACT BOX
      const boxH = 165;

      this.roundRect(ctx, 80, y, W - 160, boxH, 15);
      ctx.strokeStyle = '#E63946';
      ctx.lineWidth = 2;
      ctx.stroke();

      ctx.fillStyle = '#000';
      ctx.font = 'bold 24px Arial';
      ctx.fillText('Contact Information', 110, y + 45);

      ctx.font = '22px Arial';
      ctx.fillText(data.ownerName, 110, y + 90);
      ctx.fillText(data.phone, 110, y + 130);

      // Paw Watermark
      const pawImg = await this.loadSmartImage('https://cdn.thefurfinder.com/assets/img/paw.png');
      const centerX = (400 + 660) / 2;
      const pawSize = 80;
      ctx.drawImage(pawImg, centerX - pawSize / 2, y + 85 - pawSize / 2, pawSize, pawSize);

      // QR Code
      const qr = await this.loadSmartImage(data.qrImage);
      ctx.drawImage(qr, W - 240, y + 8, 150, 150);

      return canvas.toBuffer('image/png');
    } catch (error: any) {
      this.logger.error(`Error generating missing flyer for pet: ${data.name}`, error);
      throw new InternalServerErrorException('Failed to generate flyer image.');
    }
  }

  async generateFoundFlyer(data: FoundFlyerInput): Promise<Buffer> {
    this.logger.log(`Generating found flyer for pet type: ${data.pet_type}`);
    try {
      const W = 900;
      const H = this.getFoundHeight(data);

      const canvas = createCanvas(W, H);
      const ctx = canvas.getContext('2d');

      // 1. BACKGROUND
      ctx.fillStyle = '#f5f5f5';
      ctx.fillRect(0, 0, W, H);

      this.roundRect(ctx, 40, 40, W - 80, H - 80, 20);
      ctx.fillStyle = '#fff';
      ctx.fill();

      // 2. HEADER
      const headerH = 110;
      const gradient = ctx.createLinearGradient(60, 60, W - 60, 170);
      gradient.addColorStop(0, '#39e678');
      gradient.addColorStop(1, '#29b15b');

      this.roundRect(ctx, 60, 60, W - 120, headerH, 15);
      ctx.fillStyle = gradient;
      ctx.fill();

      ctx.fillStyle = '#fff';
      ctx.font = 'bold 44px Arial';
      ctx.textAlign = 'center';
      ctx.fillText(`Found ${data.pet_type[0].toUpperCase() + data.pet_type.slice(1)} !`, W / 2, 130);

      // 3. LAYOUT FLOW
      let y = 200;
      const leftX = 80;
      const rightX = 470;

      // 4. IMAGE
      const petImg = await this.loadSmartImage(data.petImage);
      const imgW = 350;
      const imgH = 300;

      this.roundRect(ctx, leftX, y, imgW, imgH, 18);
      ctx.save();
      ctx.clip();
      this.drawImageCover(ctx, petImg, leftX, y, imgW, imgH);
      ctx.restore();

      // 5. DETAILS
      ctx.textAlign = 'left';

      const label = (t: string, x: number, py: number) => {
        ctx.fillStyle = '#29b15b';
        ctx.font = 'bold 22px Arial';
        ctx.fillText(t, x, py);
      };

      const value = (t: string, x: number, py: number) => {
        ctx.fillStyle = '#111';
        ctx.font = `26px Arial`;
        ctx.fillText(t, x, py);
      };

      const rightWidth = W - rightX - 80;
      let ry = y + 20;
      const detailGap = 15;
      const labelH = 22;
      const valueLineH = 30;

      label('Breed', rightX, ry);
      ctx.font = '26px Arial';
      this.wrapText('#111', ctx, data.breed, rightX, ry + 28, rightWidth, valueLineH);
      ry += labelH + this.getWrappedLines(ctx, data.breed, rightWidth) * valueLineH + detailGap;

      label('Colour', rightX, ry);
      value(data.color, rightX, ry + 28);
      ry += labelH + valueLineH + detailGap;

      label('Size', rightX, ry);
      value(data.size, rightX, ry + 28);
      ry += labelH + valueLineH + detailGap;

      label('Last Seen', rightX, ry);
      value(data.lastSeen, rightX, ry + 28);
      ry += labelH + valueLineH + detailGap;

      label('Last Seen Location', rightX, ry);
      ctx.font = '26px Arial';
      this.wrapText('#111', ctx, data.location, rightX, ry + 28, rightWidth, valueLineH);
      ry += labelH + this.getWrappedLines(ctx, data.location, rightWidth) * valueLineH;

      y = Math.max(y + imgH, ry) + SPACING.lg;

      // 6. DESCRIPTION
      const descX = 80;
      const descW = W - 160;
      const lineH = 22;

      ctx.font = '22px Arial';
      const descLines = this.getWrappedLines(ctx, data.description, descW - 40);
      const descH = 30 + descLines * lineH;

      this.roundRect(ctx, descX, y, descW, descH, 12);
      ctx.fillStyle = '#f9f9f9';
      ctx.fill();

      this.wrapText('#333', ctx, data.description, descX + 20, y + 28, descW - 40, lineH);

      y += descH + SPACING.lg;

      // 7. CONTACT BOX
      const boxH = 165;

      this.roundRect(ctx, 80, y, W - 160, boxH, 15);
      ctx.strokeStyle = '#29b15b';
      ctx.lineWidth = 2;
      ctx.stroke();

      ctx.fillStyle = '#000';
      ctx.font = 'bold 24px Arial';
      ctx.fillText('Contact Information', 110, y + 45);

      ctx.font = '22px Arial';
      ctx.fillText(data.ownerName, 110, y + 90);
      ctx.fillText(data.phone, 110, y + 130);

      // Paw Watermark
      const pawImg = await this.loadSmartImage('https://cdn.thefurfinder.com/assets/img/paw.png');
      const centerX = (400 + 660) / 2;
      const pawSize = 80;
      ctx.drawImage(pawImg, centerX - pawSize / 2, y + 85 - pawSize / 2, pawSize, pawSize);

      // QR Code
      const qr = await this.loadSmartImage(data.qrImage);
      ctx.drawImage(qr, W - 240, y + 8, 150, 150);

      return canvas.toBuffer('image/png');
    } catch (error: any) {
      this.logger.error(`Error generating found flyer for pet type: ${data.pet_type}`, error);
      throw new InternalServerErrorException('Failed to generate flyer image.');
    }
  }
}
