import base64
from datetime import datetime
from io import BytesIO
from time import time

import barcode
from barcode.writer import ImageWriter
from fastapi import APIRouter
from fastapi.responses import Response
from num2words import num2words
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import ParagraphStyle
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image
from starlette.requests import Request
from starlette.templating import Jinja2Templates

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

router = APIRouter()
templates = Jinja2Templates(directory="assets/templates")

MONTHS_RU = {
    1: "января",
    2: "февраля",
    3: "марта",
    4: "апреля",
    5: "мая",
    6: "июня",
    7: "июля",
    8: "августа",
    9: "сентября",
    10: "октября",
    11: "ноября",
    12: "декабря",
}


def format_date_russian(date: datetime) -> str:
    return f"{date.day} {MONTHS_RU[date.month]} {date.year} г."


@router.post("/quickCreate", tags=[""])
async def quick_create_deal(data: dict):
    start_time = time()

    data["id"] = await mongo.get_next_id(mongo.deals_collection)
    data["createdAt"] = mongo.created_at()

    status = await mongo.statuses_collection.find_one({"id": data["statusId"]}, {"_id": False})
    if not status:
        return response({"message": "Статус не найден", "ok": False}, start_time=start_time)

    board = await mongo.boards_collection.find_one({"id": status["boardId"]}, {"_id": False})
    if not board:
        return response({"message": "Доска не найдена", "ok": False}, start_time=start_time)

    data["boardId"] = status["boardId"]
    data["projectId"] = board["projectId"]

    await mongo.deals_collection.insert_one(data)
    return response({
        "message": "Сделка создана",
        "ok": True,
        "dealId": data["id"]
    }, start_time=start_time)


@router.get("/get/{deal_id}", tags=[""])
async def get_deal(deal_id: int):
    start_time = time()

    deal = await mongo.deals_collection.find_one({
        "id": deal_id
    }, {
        "_id": False
    })

    deals = await mongo.additional_deals_data(deal, full=True)
    return response(deals[0], start_time=start_time)


@router.post("/update-general-info", tags=[""])
async def update_deal_general_info(params: dict, request: Request):
    start_time = time()
    data = params["data"]
    deal_id = params["dealId"]

    deal = await mongo.deals_collection.find_one({"id": deal_id}, {"_id": False})
    if not deal:
        return response({"message": "Сделка не найдена", "ok": False}, start_time=start_time)

    new_status = await mongo.statuses_collection.find_one({"id": data["statusId"]}, {"_id": False})
    if new_status and new_status["name"] == "Завершено":
        data["completedAt"] = mongo.created_at()

    old_status_id = deal.get("statusId")
    await mongo.deals_collection.update_one({
        "id": deal_id
    }, {
        "$set": data
    })

    if old_status_id != data["statusId"]:
        new_entry = {
            "changedAt": mongo.created_at(),
            "fromStatusId": old_status_id,
            "toStatusId": data["statusId"],
            "userId": request.state.user["id"],
            "comment": ""
        }

        await mongo.deals_collection.update_one(
            {"id": deal_id},
            {"$push": {"statusHistory": new_entry}}
        )

    return response({
        "message": "Данные сделки обновлены",
        "ok": True
    }, start_time=start_time)


@router.post("/delete", tags=[""])
async def delete_deal(params: dict):
    start_time = time()
    deal_id = params["dealId"]

    await mongo.deals_collection.delete_one({"id": deal_id})
    return response({
        "message": "Сделка удалена",
        "ok": True
    }, start_time=start_time)


@router.post("/complete", tags=[""])
async def complete_deal(params: dict):
    start_time = time()
    deal_id = params["dealId"]

    await mongo.deals_collection.update_one(
        {"id": deal_id},
        {"$set": {"isCompleted": True}}
    )

    return response({
        "message": "Сделка завершена",
        "ok": True
    }, start_time=start_time)


@router.post("/prefill", tags=[""])
async def prefill_deal(params: dict):
    start_time = time()
    old_deal_id = params["oldDealId"]
    new_deal_id = params["newDealId"]

    old_deal = await mongo.deals_collection.find_one({"id": old_deal_id}, {"_id": False})
    if not old_deal:
        return response({"message": "Сделка не найдена", "ok": False}, start_time=start_time)

    await mongo.deals_collection.update_one(
        {"id": new_deal_id},
        {"$set": {
            "name": old_deal["name"],
            "clientName": old_deal["clientName"],
            "services": old_deal.get("services", []),
            "products": old_deal.get("products", [])
        }}
    )

    return response({
        "message": "Сделка предзаполнена",
        "ok": True
    }, start_time=start_time)


@router.get("/billing-document/{deal_id}", tags=[""])
async def deal_billing_document(deal_id: int):
    pdfmetrics.registerFont(TTFont("Arial", "assets/arial.ttf"))
    pdfmetrics.registerFont(TTFont("Arial Bold", "assets/arial_bold.ttf"))

    center = ParagraphStyle(name="center", fontName="Arial", fontSize=10, alignment=1)
    bold_center = ParagraphStyle(name="bold_center", fontName="Arial Bold", fontSize=10, alignment=1)
    title = ParagraphStyle(name="title", fontName="Arial Bold", fontSize=14, alignment=1)

    deal = await mongo.deals_collection.find_one({"id": deal_id}, {"_id": False})
    buffer = BytesIO()
    doc = SimpleDocTemplate(buffer, pagesize=A4, leftMargin=20, rightMargin=20, topMargin=30, bottomMargin=20)
    elements = []

    logo = Image("assets/logo.jpg", width=152, height=56)
    logo.hAlign = "LEFT"
    elements.append(logo)
    elements.append(Spacer(1, 10))

    elements.append(Paragraph("Расчет стоимости услуг", title))
    elements.append(Spacer(1, 6))
    elements.append(Paragraph(f"№ {deal['id']} от {format_date_russian(datetime.now())}", center))
    elements.append(Paragraph("Адрес: 115516, г.Москва, ул. Промышленная 11", center))
    elements.append(Paragraph(f"Клиент: {deal.get('clientName', 'Не указан')}", center))
    elements.append(Paragraph(f"Маркетплейс: {deal.get('baseMarketplace', {}).get('name', 'Не указан')}", center))
    elements.append(Paragraph(f"Склад отгрузки: {deal.get('shippingWarehouse', 'Не указан')}", center))
    elements.append(Spacer(1, 10))

    total_sum = 0
    product_infos = []
    table_width = 450

    data_1 = [[
        Paragraph("Артикул ВБ", bold_center),
        Paragraph("Артикул продавца", bold_center),
        Paragraph("Размер", bold_center),
        Paragraph("Кол-во", bold_center),
        Paragraph("Цена", bold_center),
        Paragraph("Сумма", bold_center),
    ]]

    for product in deal.get("products", []):
        product_data = await mongo.products_collection.find_one({"id": product["productId"]}, {"_id": False})
        if not product_data:
            continue

        article = product_data.get("article", "-")
        vendor_code = product_data.get("vendorCode", "-")
        size = product_data.get("size", "-")
        quantity = product["quantity"]
        price = sum(service["price"] * service.get("quantity", 1) for service in product["services"])
        amount = quantity * price
        total_sum += amount

        data_1.append([
            Paragraph(str(article), center),
            Paragraph(str(vendor_code), center),
            Paragraph(str(size or 0), center),
            Paragraph(str(quantity or 1), center),
            Paragraph(f"{price} ₽", center),
            Paragraph(f"{amount} ₽", center),
        ])

        product_infos.append({
            "name": product_data.get("name") or "Без названия",
            "quantity": product["quantity"],
            "services": await get_services(product.get("services", []))
        })

    table_1 = Table(data_1, colWidths=[table_width / 6] * 5)
    table_1.setStyle(TableStyle([
        ("BOX", (0, 0), (-1, -1), 0.8, colors.black),
        ("INNERGRID", (0, 0), (-1, -1), 0.4, colors.grey),
        ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
        ("ALIGN", (0, 0), (-1, -1), "CENTER"),
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
        ("ROUNDEDCORNERS", (16, 16, 16, 16)),
    ]))

    elements.append(Paragraph("Товары", bold_center))
    elements.append(Spacer(1, 4))
    elements.append(table_1)
    elements.append(Spacer(1, 12))

    data_2 = [[
        Paragraph("Товар / Услуга", bold_center),
        Paragraph("Кол-во", bold_center),
        Paragraph("Цена", bold_center),
        Paragraph("Сумма", bold_center),
    ]]

    for product in product_infos:
        for service in product["services"]:
            quantity = product.get("quantity", 0)
            price = service.get("price", 0)
            amount = quantity * price
            data_2.append([
                Paragraph(f"{product['name']} {service['name']}", center),
                Paragraph(str(quantity), center),
                Paragraph(f"{price} ₽", center),
                Paragraph(f"{amount} ₽", center),
            ])

    for service in await get_services(deal.get("services", [])):
        quantity = service.get("quantity", 0)
        price = service.get("price", 0)
        amount = quantity * price
        total_sum += amount
        data_2.append([
            Paragraph(service['name'], center),
            Paragraph(str(quantity), center),
            Paragraph(f"{price} ₽", center),
            Paragraph(f"{amount} ₽", center)
        ])

    table_2 = Table(data_2, colWidths=[
        table_width * 0.4, table_width * 0.2, table_width * 0.2, table_width * 0.2
    ])

    table_2.setStyle(TableStyle([
        ("BOX", (0, 0), (-1, -1), 0.8, colors.black),
        ("INNERGRID", (0, 0), (-1, -1), 0.4, colors.grey),
        ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
        ("ALIGN", (0, 0), (-1, -1), "CENTER"),
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
        ("ROUNDEDCORNERS", (16, 16, 16, 16)),
    ]))

    elements.append(Paragraph("Услуги", bold_center))
    elements.append(Spacer(1, 4))
    elements.append(table_2)
    elements.append(Spacer(1, 12))
    elements.append(Paragraph(f"+79013618377 Ангелина К. (Т-Банк) Итого к оплате: {total_sum:,.0f} ₽".replace(",", " "), bold_center))
    elements.append(Paragraph(num2words(total_sum, lang='ru').capitalize() + " рублей", center))

    doc.build(elements)
    buffer.seek(0)
    return Response(buffer.getvalue(), media_type="application/pdf", headers={"Content-Disposition": "inline; filename=billing_document.pdf"})


@router.get("/tech-spec/{deal_id}", tags=[""])
async def deal_tech_spec(request: Request, deal_id: int):
    deal = await mongo.deals_collection.find_one({"id": deal_id}, {"_id": False})

    status = await mongo.statuses_collection.find_one({"id": deal["statusId"]}, {"_id": False})
    deal_status = status.get("name", "Статус не определен") if status else "Статус не найден"

    deal_services = await get_services(deal.get("services", []))
    deal_data = {
        "id": deal["id"],
        "name": deal.get("name") or "Без названия",
        "status": deal_status,
        "createdAt": datetime.strptime(deal["createdAt"], "%Y-%m-%dT%H:%M:%S").strftime("%d.%m.%Y, %H:%M"),
        "clientName": deal.get("clientName") or "Не указан",
        "deliveryDate": (
            datetime.strptime(deal["deliveryDate"], "%Y-%m-%dT%H:%M:%S.%f").strftime("%d.%m.%Y")
            if deal.get("deliveryDate") else "Не указана"
        ),
        "baseMarketplace": deal.get("baseMarketplace", {}).get("name") or "Не указан",
        "shippingWarehouse": deal.get("shippingWarehouse") or "Не указан",
        "products": [],
        "services": deal_services
    }

    for product in deal.get("products", []):
        product_data = await mongo.products_collection.find_one({"id": product["productId"]}, {"_id": False})
        if not product_data:
            continue

        barcode_value = product_data.get("barcodes", [None])[0]
        barcode_image = generate_barcode_base64(barcode_value) if barcode_value else None

        product_services = await get_services(product.get("services", []))
        deal_data["products"].append({
            "name": product_data.get("name") or "Без названия",
            "article": product_data.get("vendorCode") or "Нет артикула",
            "imageUrl": product_data.get("imageUrl"),
            "size": product_data.get("size") or 0,
            "quantity": product.get("quantity") or 0,
            "barcode": barcode_image,
            "services": product_services,
            "comment": product.get("comment")
        })

    return templates.TemplateResponse("deal_tech_spec.html", {"request": request, "deal": deal_data})


@router.post("/add-kit")
async def add_kits(params: dict):
    start_time = time()
    deal_id = params["dealId"]
    kit_id = params["kitId"]

    deal = await mongo.deals_collection.find_one({"id": deal_id}, {"_id": False})
    if not deal:
        return response({"message": "Сделка не найдена", "ok": False}, start_time=start_time)

    service_kit = await mongo.service_kits_collection.find_one({"id": kit_id})
    if not service_kit:
        return response({"message": "Комплект не найден", "ok": False}, start_time=start_time)

    for service in service_kit.get("services", []):
        price = service.get("price", 0)
        if service.get("priceRanges"):
            price_range = service["priceRanges"][0]
            price = price_range.get("price", price)

        deal["services"].append({
            "serviceId": service["id"],
            "price": price,
            "isFixedPrice": False,
            "quantity": 1
        })

    await mongo.deals_collection.update_one({"id": deal_id}, {"$set": {"services": deal["services"]}})
    return response({
        "message": "Сервисы из комплекта добавлены в сделку",
        "ok": True
    }, start_time=start_time)


async def get_services(services):
    service_list = []
    for service in services:
        service_data = await mongo.services_collection.find_one({"id": service["serviceId"]}, {"_id": False})
        if service_data:
            service_list.append({
                "name": service_data.get("name") or "Неизвестная услуга",
                "quantity": service.get("quantity") or 0,
                "price": service.get("price") or 0
            })

    return service_list


def generate_barcode_base64(barcode_text: str) -> str:
    buffer = BytesIO()
    code128 = barcode.get("code128", barcode_text, writer=ImageWriter())
    code128.write(buffer, {"module_height": 10.0, "font_size": 10, "quiet_zone": 2.0})
    buffer.seek(0)
    encoded = base64.b64encode(buffer.read()).decode("utf-8")
    return f"data:image/png;base64,{encoded}"
