from fastapi import FastAPI, Request, Form, status, Response
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from starlette.middleware.sessions import SessionMiddleware
from typing import List
from pydantic import BaseModel
import uuid
import escpos.printer
import os
import json
from pathlib import Path
from app.models import Task
from dotenv import load_dotenv
from PIL import Image, ImageDraw, ImageFont

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="CHANGE_THIS_SECRET")
templates = Jinja2Templates(directory="app/templates")
app.mount("/static", StaticFiles(directory="app/static"), name="static")
app.mount("/out", StaticFiles(directory="out"), name="out")

TASKS_FILE = Path("data/tasks.json")
DEBUG_PRINT_TO_IMAGE = True  # Set to True to enable card image generation instead of real printing
OUT_DIR = Path("out")
OUT_DIR.mkdir(exist_ok=True)
CARD_WIDTH_PX = 354  # 58mm * 300dpi / 25.4
CARD_HEIGHT_PX = 236  # ~40mm * 300dpi / 25.4 (adjust as needed)
FONT_PATH = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"  # Change to a font path available on your system
# Font paths for card preview (use your actual font files)
FONT_BOLD = "app/static/fonts/HealTheWebB-Regular.otf"
FONT_REGULAR = "app/static/fonts/HealTheWebA-Regular.otf"
FA_FONT = "app/static/fonts/fontawesome-free-7.1.0-desktop/otfs/Font Awesome 7 Free-Solid-900.otf"

# Simple keyword to FontAwesome unicode mapping
KEYWORD_ICONS = [
    (['kaffee', 'coffee'], '\uf0f4'),  # fa-coffee
    (['druck', 'print', 'drucker', 'printer'], '\uf02f'),   # fa-print
    (['flipchart', 'tafel', 'whiteboard'], '\uf5da'), # fa-chalkboard
    (['material', 'paket', 'box', 'lieferung', 'liefer'], '\uf466'), # fa-box
    (['reinigung', 'clean', 'putz', 'wischen'], '\uf2f9'), # fa-broom
    (['user', 'person', 'benutzer', 'name'], '\uf007'), # fa-user
    (['aufgabe', 'task', 'todo'], '\uf0ae'), # fa-tasks
    (['reparatur', 'reparieren', 'repair', 'defekt', 'fix', 'wartung', 'maintenance'], '\uf0ad'), # fa-wrench
    (['upgrade', 'update', 'aufrüsten', 'erneuern', 'verbessern'], '\uf0ee'), # fa-arrow-up
    (['laser', 'lasercutter'], '\uf0c3'), # fa-cut
    (['3d', 'druck', 'drucker', '3d-druck', '3d-printer'], '\uf1b2'), # fa-cube
    (['cnc', 'fräse', 'fräsen', 'fräser'], '\uf85c'), # fa-tools
    (['löt', 'löten', 'solder'], '\uf5fc'), # fa-tools
    (['computer', 'pc', 'laptop', 'rechner'], '\uf109'), # fa-desktop
    (['software', 'programm', 'app', 'installation'], '\uf121'), # fa-code
    (['kabel', 'stecker', 'anschluss'], '\uf1e6'), # fa-plug
    (['werkzeug', 'tool', 'tools', 'schraubenzieher', 'hammer'], '\uf7d9'), # fa-hammer
    (['strom', 'elektrik', 'elektronik', 'spannung'], '\uf0e7'), # fa-bolt
    (['sensor', 'arduino', 'microcontroller', 'raspberry', 'pi', 'esp32'], '\uf2db'), # fa-microchip
    (['holz', 'wood', 'säge', 'sägen'], '\uf6e3'), # fa-tree
    (['metall', 'blech', 'schweißen', 'schweissen'], '\uf670'), # fa-industry
    (['projekt', 'projektarbeit'], '\uf542'), # fa-project-diagram
]

def load_tasks():
    if TASKS_FILE.exists():
        with open(TASKS_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)
            return [Task(**item) for item in data]
    return []

def save_tasks():
    with open(TASKS_FILE, "w", encoding="utf-8") as f:
        json.dump([t.dict() for t in tasks], f, ensure_ascii=False, indent=2)

tasks: List[Task] = load_tasks()
load_dotenv()
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD")  # Must be set in .env
N_APPROVED = 5  # Number of last approved tasks to show

def get_icon_for_task(content):
    c = content.lower()
    for keywords, icon in KEYWORD_ICONS:
        if any(k in c for k in keywords):
            return icon
    return '\uf328'  # fa-sticky-note as default

@app.get("/")
def index(request: Request):
    approved_tasks = [t for t in tasks if t.status == "approved"][-N_APPROVED:]
    login_result = request.session.pop("login_result", None)
    print_result = request.session.pop("print_result", None)
    preview_image = request.session.pop("preview_image", None)
    return templates.TemplateResponse(
        "index.html",
        {
            "request": request,
            "tasks": tasks,
            "admin": request.session.get("admin", False),
            "approved_tasks": approved_tasks,
            "login_result": login_result,
            "print_result": print_result,
            "preview_image": preview_image,
        },
    )

@app.post("/submit")
def submit_task(request: Request, content: str = Form(...), user: str = Form(...), priority: int = Form(...)):
    task = Task.create(content=content, user=user, priority=priority)
    tasks.append(task)
    save_tasks()
    return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)

@app.post("/approve/{task_id}")
def approve_task(request: Request, task_id: str):
    if not request.session.get("admin"):
        return Response("Nicht autorisiert", status_code=401)
    for task in tasks:
        if task.id == task_id and task.status == "pending":
            task.status = "approved"
    save_tasks()
    return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)


import logging

def load_font(path, size, fallback=None, font_label=None):
    try:
        return ImageFont.truetype(path, size)
    except Exception as e:
        logging.error(f"Failed to load font '{path}' ({font_label or ''}): {e}")
        if fallback:
            logging.info(f"Falling back to default font for {font_label or path}")
            return fallback
        return ImageFont.load_default()

@app.post("/print/{task_id}")
def print_task(request: Request, task_id: str):
    msg = None
    preview_img = None
    font_error_msgs = []
    for task in tasks:
        if task.id == task_id:
            if (task.status == "approved") or (task.status == "printed" and request.session.get("admin")):
                try:
                    if DEBUG_PRINT_TO_IMAGE:
                        # Generate styled card image with icon
                        img = Image.new("L", (CARD_WIDTH_PX, CARD_HEIGHT_PX), 255)
                        draw = ImageDraw.Draw(img)
                        draw.rectangle([(0,0),(CARD_WIDTH_PX-1,CARD_HEIGHT_PX-1)], outline=0, width=4)

                        # Robust font loading
                        font_title = load_font(FONT_BOLD, 36, font_label="title")
                        font_label_f = load_font(FONT_BOLD, 18, font_label="label")
                        font_text = load_font(FONT_REGULAR, 22, font_label="text")
                        font_icon = load_font(FA_FONT, 48, font_label="icon")

                        # Prepare content for line wrapping
                        import textwrap
                        y = 24
                        max_text_width = CARD_WIDTH_PX - 40
                        content_lines = []
                        if font_title:
                            wrapper = textwrap.TextWrapper(width=24)
                            lines = wrapper.wrap(task.content)
                            for line in lines:
                                # Check pixel width, wrap further if needed
                                while font_title.getlength(line) > max_text_width:
                                    # Reduce by one word at a time
                                    split = line.rsplit(' ', 1)
                                    if len(split) == 2:
                                        content_lines.append(split[0])
                                        line = split[1]
                                    else:
                                        # Single long word
                                        content_lines.append(line[:20])
                                        line = line[20:]
                                content_lines.append(line)
                        else:
                            content_lines = textwrap.wrap(task.content, width=24)

                        # Center lines vertically

                        # Calculate text heights using getbbox
                        def get_text_height(font, text):
                            try:
                                bbox = font.getbbox(text)
                                return bbox[3] - bbox[1]
                            except Exception:
                                return 24

                        line_heights = [get_text_height(font_title, line) for line in content_lines]
                        total_text_height = sum(line_heights) + (len(content_lines)-1)*2
                        y = (CARD_HEIGHT_PX - total_text_height) // 2 - 10
                        for idx, line in enumerate(content_lines):
                            try:
                                w = font_title.getlength(line)
                            except Exception:
                                w = len(line) * 18
                            draw.text(((CARD_WIDTH_PX-w)//2, y), line, font=font_title, fill=0)
                            y += line_heights[idx] + 2

                        # User (centered below text)
                        user_label = f"Von: {task.user}"
                        try:
                            w = font_label_f.getlength(user_label)
                            user_h = get_text_height(font_label_f, user_label)
                        except Exception:
                            w = len(user_label) * 10
                            user_h = 18
                        draw.text(((CARD_WIDTH_PX-w)//2, y+8), user_label, font=font_label_f, fill=0)
                        y += user_h + 12

                        # Priority (centered below user)
                        prio_label = f"Priorität: {task.priority}"
                        try:
                            w = font_label_f.getlength(prio_label)
                        except Exception:
                            w = len(prio_label) * 10
                        draw.text(((CARD_WIDTH_PX-w)//2, y), prio_label, font=font_label_f, fill=0)

                        # Icon in lower right corner
                        icon = get_icon_for_task(task.content)
                        if font_icon:
                            icon_bbox = font_icon.getbbox(icon)
                            icon_w = icon_bbox[2] - icon_bbox[0]
                            icon_h = icon_bbox[3] - icon_bbox[1]
                            icon_x = CARD_WIDTH_PX - icon_w - 16
                            icon_y = CARD_HEIGHT_PX - icon_h - 12
                            draw.text((icon_x, icon_y), icon, font=font_icon, fill=0)
                        else:
                            font_error_msgs.append("[!] Icon font missing")

                        # If any font errors, show a warning on the card
                        if font_error_msgs:
                            draw.text((10, CARD_HEIGHT_PX-24), ", ".join(font_error_msgs), font=ImageFont.load_default(), fill=128)

                        img_path = OUT_DIR / f"task_{task.id}.png"
                        img.save(img_path)
                        preview_img = str(img_path)
                        msg = "success"
                        task.status = "printed"
                    else:
                        printer = escpos.printer.Serial(devfile="/dev/ttyUSB0", baudrate=19200, timeout=1)
                        printer.text(f"Task: {task.content}\nVon: {task.user}\nPriorität: {task.priority}\n")
                        printer.cut()
                        task.status = "printed"
                        msg = "success"
                except Exception as e:
                    print(f"Printer error: {e}")
                    msg = f"error:{e}"
            else:
                msg = "not_allowed"
    save_tasks()
    request.session["print_result"] = msg
    if preview_img:
        request.session["preview_image"] = preview_img
    return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)

@app.post("/admin/login")
def admin_login(request: Request, password: str = Form(...)):
    if password == ADMIN_PASSWORD:
        request.session["admin"] = True
        request.session["login_result"] = "success"
    else:
        request.session["admin"] = False
        request.session["login_result"] = "fail"
    return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)

@app.post("/admin/logout")
def admin_logout(request: Request):
    request.session["admin"] = False
    return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)
