from __future__ import annotations

import base64
from datetime import date, datetime, timezone
from io import BytesIO
from pathlib import Path
from urllib.request import Request, urlopen

from fpdf import FPDF

from app.models import NimitSevakReceiptRequest
from app.services.font_resolver import resolve_gujarati_font_paths

REGULAR_FONT_NAME = "GujaratiRegular"
BOLD_FONT_NAME = "GujaratiBold"

BODY_TEXT = (35, 31, 32)
PAGE_WIDTH_MM = 210
PAGE_HEIGHT_MM = 297
GUJARATI_DIGIT_TRANSLATION = str.maketrans("0123456789", "૦૧૨૩૪૫૬૭૮૯")

# Layout controls. Adjust x, y, font_size, and font_name for text placement.
FORM_NUMBER_BOX = {
    "x": 179.0,
    "y": 44,
    "font_size": 12,
    "font_name": BOLD_FONT_NAME,
}

PHOTO_FRAME = {
    "x": 161.0,
    "y": 53.0,
    "w": 42.0,
    "h": 50.0,
    "inset_x": 1.8,
    "inset_y": 1.8,
    "scale": 0.99,
}

FIELD_NAME = {
    "x": 8,
    "y": 65,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
}

FIELD_ADDRESS = {
    "x": 8,
    "y": 74,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
    "width": 150,
    "line_height": 5.4,
}

FIELD_AADHAR = {
    "x": 8,
    "y": 92,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
}

FIELD_MOBILE = {
    "x": 90.0,
    "y": 92,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
}

FIELD_OCCUPATION = {
    "x": 8,
    "y": 101,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
}

FIELD_AGE = {
    "x": 90.0,
    "y": 101,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
}

FIELD_PRADESH = {
    "x": 8,
    "y": 110,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
}

# FIELD_MANDAL = {
#     "x": 64.5,
#     "y": 104,
#     "font_size": 14,
#     "font_name": BOLD_FONT_NAME,
# }

FIELD_REFERENCE = {
    "x": 90.0,
    "y": 110,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
}

SEVA_SECTION = {
    "x": 8.0,
    "y": 130.0,
    "width": 180.0,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
    "line_height": 6.3,
}

PRAYER_POINTS_SECTION = {
    "x": 8.0,
    "y": 147.0,
    "width": 180.0,
    "font_size": 14,
    "font_name": REGULAR_FONT_NAME,
    "line_height": 6.3,
    "gap": 2.2,
}


class ReceiptPDF(FPDF):
    pass


def _build_pdf() -> ReceiptPDF:
    pdf = ReceiptPDF(orientation="P", unit="mm", format="A4")
    pdf.set_auto_page_break(auto=False)
    pdf.set_margins(12, 12, 12)
    pdf.add_page()
    pdf.set_text_shaping(True)
    return pdf


def _register_fonts(pdf: ReceiptPDF) -> None:
    regular_path, bold_path = resolve_gujarati_font_paths()
    pdf.add_font(REGULAR_FONT_NAME, style="", fname=str(regular_path))
    pdf.add_font(BOLD_FONT_NAME, style="", fname=str(bold_path))


def generate_receipt_number() -> str:
    return f"NS-{datetime.now(timezone.utc).microsecond % 100000:05d}"


def _normalize_text(value: str | None, fallback: str = "-") -> str:
    text = str(value or "").strip()
    return text or fallback


def _format_list(values: list[str], fallback: str = "-") -> str:
    cleaned = [_normalize_text(value, "") for value in values]
    items = [value for value in cleaned if value]
    return ", ".join(items) if items else fallback


def _format_gujarati_date(value: date | None) -> str:
    if value is None:
        return "-"
    return value.strftime("%d/%m/%Y").translate(GUJARATI_DIGIT_TRANSLATION)


def _build_seva_list_text(payload: NimitSevakReceiptRequest) -> str:
    seva_items = list(payload.available_sevas)
    if payload.other_seva_details:
        seva_items.append(payload.other_seva_details)
    return f"સેવા યાદી :- {_format_list(seva_items)}"


def _build_prayer_points(payload: NimitSevakReceiptRequest) -> list[str]:
    seva_from = _format_gujarati_date(payload.seva_available_from)
    seva_to = _format_gujarati_date(payload.seva_available_to)
    vehicle_from = _format_gujarati_date(payload.vehicle_available_from)
    vehicle_to = _format_gujarati_date(payload.vehicle_available_to)
    drivable_vehicles = _format_list(payload.drivable_vehicles)
    available_vehicles = _format_list(payload.available_vehicles)

    points = [
        f"૧. હે સ્વામીજી, હું તારીખ {seva_from} થી {seva_to} સુધી મ.૨૮, ૪૧ પ્રમાણે નિમિત્ત બની આપને રાજી કરવા સેવામાં આવી શકીશ.",
        f"૨. મને {drivable_vehicles} ચલાવતા આવડે છે.",
        f"૩. હે સ્વામીજી, મારી પાસે {available_vehicles} છે. જે હું તારીખ {vehicle_from} થી {vehicle_to} સેવામાં લાવી શકું તેમ છું.",
    ]

    if payload.night_shift_available:
        points.append(
            "૪. હે સ્વામીજી!, દિવસે હું મારા વ્યવહાર/ વ્યવસાયમાં વ્યસ્ત હોઉ છું , પરંતુ મારે નિમિત બનવું છે , હું રાત્રે સેવા માં ચોક્કસ આવી શકીશ"
        )

    return points


def _decode_image(data: str | None):
    if not data:
        return None
    payload = data.split(",", 1)[1] if "," in data else data
    try:
        return BytesIO(base64.b64decode(payload))
    except Exception:
        return None


def _load_remote_image(url: str | None):
    if not url:
        return None

    try:
        request = Request(url, headers={"User-Agent": "sdm-receipt-service/1.0"})
        with urlopen(request, timeout=15) as response:
            return BytesIO(response.read())
    except Exception:
        return None


def _load_local_image(path_value: str | None):
    if not path_value:
        return None

    try:
        candidate = Path(path_value).expanduser()
        if candidate.exists() and candidate.is_file():
            return str(candidate)
    except Exception:
        return None

    return None


def _load_form_background_image() -> str | None:
    project_root = Path(__file__).resolve().parents[2]
    background_path = project_root / "assets" / "images" / "form.jpg"
    if background_path.exists():
        return str(background_path)
    return None


def _set_text_color(pdf: ReceiptPDF, rgb: tuple[int, int, int]) -> None:
    pdf.set_text_color(*rgb)


def _draw_centered_image(pdf: ReceiptPDF, image, x: float, y: float, w: float, h: float, *, scale: float = 0.96) -> None:
    target_w = w * scale
    target_h = h * scale
    offset_x = x + ((w - target_w) / 2)
    offset_y = y + ((h - target_h) / 2)
    pdf.image(image, x=offset_x, y=offset_y, w=target_w, h=target_h, keep_aspect_ratio=True)


def _draw_form_number_box(pdf: ReceiptPDF, form_number: str) -> None:
    normalized_number = _normalize_text(form_number, "")
    if normalized_number:
        _set_text_color(pdf, BODY_TEXT)
        pdf.set_font(str(FORM_NUMBER_BOX["font_name"]), size=FORM_NUMBER_BOX["font_size"])
        pdf.set_xy(FORM_NUMBER_BOX["x"], FORM_NUMBER_BOX["y"])
        pdf.cell(0, 4.2, text=normalized_number)


def _draw_text(
    pdf: ReceiptPDF,
    text: str,
    x: float,
    y: float,
    font_size: float,
    *,
    font_name: str = REGULAR_FONT_NAME,
    width: float | None = None,
    line_height: float = 5.0,
) -> float:
    value = _normalize_text(text, "")
    if not value:
        return y

    _set_text_color(pdf, BODY_TEXT)
    pdf.set_font(font_name, size=font_size)
    pdf.set_xy(x, y)
    if width is None:
        pdf.cell(0, line_height, text=value)
        return y + line_height
    else:
        pdf.multi_cell(width, line_height, text=value)
        return pdf.get_y()


def generate_nimit_sevak_pdf(payload: NimitSevakReceiptRequest, receipt_number: str) -> bytes:
    pdf = _build_pdf()
    _register_fonts(pdf)
    pdf.set_title(f"Nimit Sevak Gujarati Receipt - {receipt_number}")

    background_image = _load_form_background_image()
    photo_image = (
        _decode_image(payload.photo_image_base64)
        or _load_local_image(payload.profile_photo_url)
        or _load_remote_image(payload.profile_photo_url)
    )
    form_number = _normalize_text(payload.form_no or payload.receipt_number or receipt_number, "")

    if background_image:
        pdf.image(background_image, x=0, y=0, w=PAGE_WIDTH_MM, h=PAGE_HEIGHT_MM)

    if photo_image:
        _draw_centered_image(
            pdf,
            photo_image,
            x=PHOTO_FRAME["x"] + PHOTO_FRAME["inset_x"],
            y=PHOTO_FRAME["y"] + PHOTO_FRAME["inset_y"],
            w=PHOTO_FRAME["w"] - (PHOTO_FRAME["inset_x"] * 2),
            h=PHOTO_FRAME["h"] - (PHOTO_FRAME["inset_y"] * 2),
            scale=PHOTO_FRAME["scale"],
        )

    _draw_form_number_box(pdf, form_number)

    _draw_text(pdf, f"નામ :- શ્રી {_normalize_text(payload.applicant_name, '')}", FIELD_NAME["x"], FIELD_NAME["y"], FIELD_NAME["font_size"], font_name=str(FIELD_NAME["font_name"]))
    _draw_text(
        pdf,
        f"રહેઠાણ :- {_normalize_text(payload.address, '')}",
        FIELD_ADDRESS["x"],
        FIELD_ADDRESS["y"],
        FIELD_ADDRESS["font_size"],
        font_name=str(FIELD_ADDRESS["font_name"]),
        width=FIELD_ADDRESS["width"],
        line_height=FIELD_ADDRESS["line_height"],
    )
    _draw_text(pdf, f"આધારકાર્ડ નં. :- {_normalize_text(payload.aadhar_card_no, '')}", FIELD_AADHAR["x"], FIELD_AADHAR["y"], FIELD_AADHAR["font_size"], font_name=str(FIELD_AADHAR["font_name"]))
    _draw_text(pdf, f"મો નં. :- {_normalize_text(payload.mobile_no, '')}", FIELD_MOBILE["x"], FIELD_MOBILE["y"], FIELD_MOBILE["font_size"], font_name=str(FIELD_MOBILE["font_name"]))
    _draw_text(pdf, f"વ્યવસાય :- {_normalize_text(payload.occupation, '')}", FIELD_OCCUPATION["x"], FIELD_OCCUPATION["y"], FIELD_OCCUPATION["font_size"], font_name=str(FIELD_OCCUPATION["font_name"]))
    _draw_text(pdf, f"ઉંમર :- {_normalize_text(payload.age, '')}, {_normalize_text(payload.gender, '')}", FIELD_AGE["x"], FIELD_AGE["y"], FIELD_AGE["font_size"], font_name=str(FIELD_AGE["font_name"]))
    _draw_text(pdf, f"પ્રદેશ :- {_normalize_text(payload.pradesh, '')}", FIELD_PRADESH["x"], FIELD_PRADESH["y"], FIELD_PRADESH["font_size"], font_name=str(FIELD_PRADESH["font_name"]))
    _draw_text(pdf, f"કાર્યકર્તા નું નામ :- {_normalize_text(payload.reference_person_name, '')}", FIELD_REFERENCE["x"], FIELD_REFERENCE["y"], FIELD_REFERENCE["font_size"], font_name=str(FIELD_REFERENCE["font_name"]))

    _draw_text(
        pdf,
        _build_seva_list_text(payload),
        SEVA_SECTION["x"],
        SEVA_SECTION["y"],
        SEVA_SECTION["font_size"],
        font_name=str(SEVA_SECTION["font_name"]),
        width=SEVA_SECTION["width"],
        line_height=SEVA_SECTION["line_height"],
    )

    current_y = PRAYER_POINTS_SECTION["y"]

    for point in _build_prayer_points(payload):
        current_y = _draw_text(
            pdf,
            point,
            PRAYER_POINTS_SECTION["x"],
            current_y,
            PRAYER_POINTS_SECTION["font_size"],
            font_name=str(PRAYER_POINTS_SECTION["font_name"]),
            width=PRAYER_POINTS_SECTION["width"],
            line_height=PRAYER_POINTS_SECTION["line_height"],
        )
        current_y += PRAYER_POINTS_SECTION["gap"]

    return bytes(pdf.output())
