import base64
import os
import textwrap
from io import BytesIO
from time import time

import fitz
from PyPDF2 import PdfReader, PdfWriter
from fastapi import APIRouter, UploadFile, File
from reportlab.graphics.barcode import code128
from reportlab.lib.units import mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfgen import canvas
from starlette.exceptions import HTTPException
from starlette.requests import Request

from app import mongo
from app.utils.response_util import response

router = APIRouter()

STATIC_FOLDER = "static"
BARCODES_FOLDER = os.path.join(STATIC_FOLDER, "barcodes")
os.makedirs(BARCODES_FOLDER, exist_ok=True)


@router.post("/get", tags=[""])
async def get_barcode(params: dict):
    start_time = time()
    return response({
        "barcode": params["barcode"]
    }, start_time=start_time)


@router.post("/add", tags=[""])
async def add_barcode(params: dict):
    start_time = time()

    product = await mongo.products_collection.find_one({
        "id": params["productId"]
    }, {
        "_id": False
    })

    barcodes = product["barcodes"]
    barcodes.append(params["barcode"])

    await mongo.products_collection.update_one({
        "id": params["productId"]
    }, {
        "$set": {"barcodes": barcodes}
    })

    return response({
        "message": "Штрихкод добавлен",
        "ok": True
    }, start_time=start_time)


@router.get("/exists", tags=[""])
async def barcode_exists(product_id: int, barcode: str):
    start_time = time()

    product = await mongo.products_collection.find_one({
        "id": product_id
    }, {
        "_id": False
    })

    return response({
        "exists": barcode in product["barcodes"]
    }, start_time=start_time)


@router.post("/get-pdf", tags=[""])
async def get_barcode_pdf(params: dict):
    product_id = params["productId"]
    quantity = params['quantity']

    pdf_path = os.path.join(BARCODES_FOLDER, f"{product_id}.pdf")
    if os.path.exists(pdf_path):
        with open(pdf_path, "rb") as file:
            existing_pdf = PdfReader(file)

            buffer = BytesIO()
            new_pdf = PdfWriter()

            for _ in range(quantity):
                for page_num in range(len(existing_pdf.pages)):
                    page = existing_pdf.pages[page_num]
                    new_pdf.add_page(page)

            new_pdf.write(buffer)
            buffer.seek(0)

            return {
                "base64String": base64.b64encode(buffer.getvalue()).decode("utf-8"),
                "mimeType": "application/pdf"
            }

    product = await mongo.products_collection.find_one(
        {"id": product_id}, {"_id": False}
    )

    client = await mongo.clients_collection.find_one(
        {"id": product["clientId"]}, {"_id": False}
    )

    barcode_template = await mongo.templates_collection.find_one(
        {"id": product["barcodeTemplate"]["id"]} if product["barcodeTemplate"] else {}, {"_id": False}
    )

    attributes = {
        attribute["key"]: attribute["name"]
        for attribute in barcode_template["attributes"]
    }

    buffer = BytesIO()
    pdf = canvas.Canvas(buffer, pagesize=(
        barcode_template["size"]["width"] * mm,
        barcode_template["size"]["height"] * mm
    ))

    pdf.setTitle("Product Barcode Information")
    pdfmetrics.registerFont(TTFont('Arial', 'assets/arial.ttf'))
    pdfmetrics.registerFont(TTFont('Arial Bold', 'assets/arial_bold.ttf'))

    for _ in range(quantity):
        name_lines = textwrap.wrap(product["name"], width=24)
        text_y = 36 * mm

        pdf.setFont("Arial Bold", 9)
        for line in name_lines:
            pdf.drawCentredString(29 * mm, text_y, line)
            text_y -= 3 * mm

        pdf.setFont("Arial", 7)
        if "article" in attributes:
            pdf.drawString(6 * mm, text_y, f"{attributes['article']}: {product['article']}")
            text_y -= 3 * mm

        if "brand" in attributes and product.get("brand"):
            pdf.drawString(6 * mm, text_y, f"{attributes['brand']}: {product['brand']}")
            text_y -= 3 * mm

        if "client.name" in attributes and client:
            pdf.drawString(6 * mm, text_y, client["name"])
            text_y -= 3 * mm

        if "color" in attributes and product.get("color"):
            pdf.drawString(6 * mm, text_y, f"{attributes['color']}: {product['color']}")
            text_y -= 3 * mm

        if "size" in attributes and product.get("size"):
            pdf.drawString(6 * mm, text_y, f"{attributes['size']}: {product['size']}")
            text_y -= 3 * mm

        barcode = code128.Code128(params["barcode"], barWidth=0.3 * mm, barHeight=10 * mm)
        barcode.drawOn(pdf, 5 * mm, text_y - 8 * mm)

        pdf.drawCentredString(29 * mm, text_y - 11 * mm, params["barcode"])
        pdf.showPage()

    pdf.save()

    buffer.seek(0)
    return {
        "base64String": base64.b64encode(buffer.getvalue()).decode("utf-8"),
        "mimeType": "application/pdf"
    }


@router.post("/upload-image/{product_id}", tags=[""])
async def upload_product_barcode_image(
        request: Request,
        product_id: int,
        upload_file: UploadFile = File(...)
):
    start_time = time()

    product = await mongo.products_collection.find_one({"id": product_id})
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")

    extension = os.path.splitext(upload_file.filename)[1] or ".pdf"
    filename = f"{product_id}{extension}"
    file_path = os.path.join(BARCODES_FOLDER, filename)

    with open(file_path, "wb") as file:
        file.write(await upload_file.read())

    document = fitz.open(file_path)
    page = document.load_page(0)
    pixmap = page.get_pixmap()
    output_image_filename = f"{product_id}.png"
    output_image_path = os.path.join(BARCODES_FOLDER, output_image_filename)
    pixmap.save(output_image_path)
    document.close()

    barcode_image_url = f"{str(request.base_url).rstrip('/')}/api/files/barcodes/{output_image_filename}"
    return response({
        "barcodeImageUrl": barcode_image_url,
        "message": "Штрихкод успешно загружен!",
        "ok": True
    }, start_time=start_time)


@router.post("/delete-image/{product_id}", tags=[""])
async def delete_product_barcode_image(product_id: int):
    start_time = time()

    pdf_path = os.path.join(BARCODES_FOLDER, f"{product_id}.pdf")
    png_path = os.path.join(BARCODES_FOLDER, f"{product_id}.png")

    if not os.path.exists(pdf_path):
        raise HTTPException(status_code=404, detail="Barcode PDF not found")

    if os.path.exists(pdf_path):
        os.remove(pdf_path)

    if os.path.exists(png_path):
        os.remove(png_path)

    return response({
        "message": "Штрихкод успешно удалён!",
        "ok": True
    }, start_time=start_time)


@router.post("/image/{product_id}", tags=[""])
async def get_product_barcode_image(request: Request, product_id: int):
    start_time = time()

    image_filename = f"{product_id}.png"
    image_path = os.path.join(BARCODES_FOLDER, image_filename)

    if not os.path.exists(image_path):
        raise HTTPException(status_code=404, detail="Barcode image not found")

    barcode_image_url = f"{str(request.base_url).rstrip('/')}/api/files/barcodes/{image_filename}"
    return response({
        "barcodeImageUrl": barcode_image_url,
        "ok": True,
        "message": "Штрихкод найден!"
    }, start_time=start_time)
