import json
import os
import sys
from datetime import datetime

def _app_root():
    # quando virar .exe (PyInstaller)
    if getattr(sys, "frozen", False):
        return os.path.dirname(sys.executable)
    # quando rodar via python normal
    return os.path.dirname(os.path.abspath(__file__))

APP_ROOT = _app_root()

DATA_DIR = os.path.join(APP_ROOT, "data")
BOXES_FILE = os.path.join(DATA_DIR, "boxes.json")


def _now():
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


def _ensure_data_dir():
    os.makedirs(DATA_DIR, exist_ok=True)


def load_boxes():
    """
    Retorna dict:
    {
      "last_box_id": int,
      "boxes": list
    }
    """
    _ensure_data_dir()

    if not os.path.isfile(BOXES_FILE):
        data = {"last_box_id": 0, "boxes": []}
        save_boxes(data)
        return data

    with open(BOXES_FILE, "r", encoding="utf-8") as f:
        return json.load(f)


def save_boxes(data: dict):
    _ensure_data_dir()
    with open(BOXES_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)


def generate_next_box_id(data: dict) -> str:
    """
    Incrementa last_box_id e retorna ID formatado.
    """
    data["last_box_id"] += 1
    return f"{data['last_box_id']:06d}"


def create_box(model: str, lote: str, capacity: int = 100) -> dict:
    """
    Cria nova caixa ABERTA.
    """
    data = load_boxes()

    box_id = generate_next_box_id(data)

    box = {
        "id": box_id,
        "model": model,
        "lote": lote,
        "capacity": capacity,
        "count": 0,
        "status": "ABERTA",
        "created_at": _now(),
        "updated_at": _now(),
    }

    data["boxes"].append(box)
    save_boxes(data)

    return box


def close_box(box_id: str) -> dict:
    """
    Fecha manualmente uma caixa ABERTA (mantém a contagem atual).
    """
    data = load_boxes()

    for box in data["boxes"]:
        if box["id"] == box_id:
            if box["status"] != "ABERTA":
                raise RuntimeError("Caixa não está aberta")

            box["status"] = "FECHADA"
            box["updated_at"] = _now()

            save_boxes(data)
            return box

    raise RuntimeError("Caixa não encontrada")


def get_open_boxes(model: str | None = None) -> list:
    """
    Retorna caixas ABERTAS.
    Se model for informado, filtra por modelo.
    """
    data = load_boxes()
    boxes = [b for b in data["boxes"] if b["status"] == "ABERTA"]

    if model:
        boxes = [b for b in boxes if b["model"] == model]

    # mais recentes primeiro
    boxes.sort(key=lambda b: b.get("id", ""), reverse=True)
    return boxes


def get_closed_boxes(model: str | None = None) -> list:
    """
    Retorna caixas FECHADAS.
    Se model for informado, filtra por modelo.
    """
    data = load_boxes()
    boxes = [b for b in data["boxes"] if b["status"] == "FECHADA"]

    if model:
        boxes = [b for b in boxes if b["model"] == model]

    # mais recentes primeiro (id é incremental)
    boxes.sort(key=lambda b: b.get("id", ""), reverse=True)
    return boxes


def increment_box(box_id: str) -> dict:
    """
    Incrementa contador da caixa.
    Fecha automaticamente ao atingir capacity.
    """
    data = load_boxes()

    for box in data["boxes"]:
        if box["id"] == box_id:
            if box["status"] != "ABERTA":
                raise RuntimeError("Caixa não está aberta")

            box["count"] += 1
            box["updated_at"] = _now()

            if box["count"] >= box["capacity"]:
                box["count"] = box["capacity"]
                box["status"] = "FECHADA"

            save_boxes(data)
            return box

    raise RuntimeError("Caixa não encontrada")


def get_box_by_id(box_id: str) -> dict | None:
    data = load_boxes()
    for box in data["boxes"]:
        if box["id"] == box_id:
            return box
    return None
