"""HTTP C4 : contrats photocopieurs et consommables partagés."""
from datetime import date

from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required
from sqlalchemy.orm import joinedload, selectinload

from ..core.authorization import permission_required
from ..core.models import (
    Company, Consumable, ConsumableOrder, ConsumableUsage, ContractEquipmentAssignment,
    Equipment, EquipmentConsumable, PhotocopierContract, PhotocopierContractPeriod,
)
from ..core.services.c4_service import (
    C4DomainError, assign_equipment_to_contract, calculate_contract_usage,
    cancel_consumable_order, close_photocopier_contract, create_consumable_order, create_contract,
    evaluate_quota_alert, estimate_consumable_lifetime, estimate_consumable_stock_coverage,
    project_contract_usage, receive_consumable_order, record_consumable_issue,
)
from ..extensions import db

c4_bp = Blueprint("c4", __name__, template_folder="templates")


def _parse_date(value):
    return date.fromisoformat(value) if value else None


@c4_bp.get("/contracts")
@login_required
@permission_required("contract.view")
def contracts():
    rows = PhotocopierContract.query.order_by(PhotocopierContract.start_date.desc()).all()
    return render_template("c4/contracts.html", contracts=rows)


@c4_bp.get("/equipment/<int:equipment_id>")
@login_required
@permission_required("patrimoine.view")
def equipment_detail(equipment_id):
    equipment = Equipment.query.get_or_404(equipment_id)
    assignments = ContractEquipmentAssignment.query.filter_by(equipment_id=equipment.id).order_by(ContractEquipmentAssignment.entry_date.desc()).all()
    links = EquipmentConsumable.query.filter_by(equipment_id=equipment.id).all()
    return render_template("c4/equipment_detail.html", equipment=equipment, assignments=assignments, links=links)


@c4_bp.route("/contracts/new", methods=["GET", "POST"])
@login_required
@permission_required("contract.manage")
def new_contract():
    companies = Company.query.order_by(Company.name).all()
    if request.method == "POST":
        try:
            contract = create_contract(
                supplier_id=request.form.get("supplier_id", type=int), name=request.form.get("name", "").strip(),
                reference=request.form.get("reference") or None, start_date=_parse_date(request.form.get("start_date")),
                end_date=_parse_date(request.form.get("end_date")), anniversary_month=request.form.get("anniversary_month", type=int),
                anniversary_day=request.form.get("anniversary_day", type=int),
                quota_start_date=_parse_date(request.form.get("quota_start_date")), notes=request.form.get("notes"),
            )
            flash("Contrat photocopieur créé.", "success")
            return redirect(url_for("c4.contract_detail", contract_id=contract.id))
        except (ValueError, TypeError, C4DomainError) as exc:
            db.session.rollback()
            flash(str(exc), "danger")
    return render_template("c4/contract_form.html", companies=companies)


@c4_bp.get("/contracts/<int:contract_id>")
@login_required
@permission_required("contract.view")
def contract_detail(contract_id):
    contract = (PhotocopierContract.query.options(
        joinedload(PhotocopierContract.supplier),
        selectinload(PhotocopierContract.periods),
        selectinload(PhotocopierContract.assignments).selectinload(ContractEquipmentAssignment.equipment),
    ).get_or_404(contract_id))
    periods = contract.periods
    projections = {period.id: project_contract_usage(period) for period in periods}
    data = {period.id: projections[period.id]["usage"] for period in periods}
    return render_template("c4/contract_detail.html", contract=contract, periods=periods, usage=data, projections=projections)


@c4_bp.post("/contracts/<int:contract_id>/equipment")
@login_required
@permission_required("contract.manage")
def contract_equipment(contract_id):
    contract = PhotocopierContract.query.get_or_404(contract_id)
    equipment = Equipment.query.get_or_404(request.form.get("equipment_id", type=int))
    try:
        assign_equipment_to_contract(contract=contract, equipment=equipment, entry_date=_parse_date(request.form.get("entry_date")), exit_date=_parse_date(request.form.get("exit_date")))
        flash("Machine rattachée au contrat.", "success")
    except (ValueError, C4DomainError) as exc:
        db.session.rollback()
        flash(str(exc), "danger")
    return redirect(url_for("c4.contract_detail", contract_id=contract_id))


@c4_bp.post("/contracts/<int:contract_id>/close")
@login_required
@permission_required("contract.manage")
def close_contract(contract_id):
    contract = PhotocopierContract.query.get_or_404(contract_id)
    try:
        close_photocopier_contract(contract=contract, closed_at=_parse_date(request.form.get("closed_at")))
        flash("Contrat clos ; son historique est conservé.", "success")
    except C4DomainError as exc:
        db.session.rollback()
        flash(str(exc), "danger")
    return redirect(url_for("c4.contract_detail", contract_id=contract_id))


@c4_bp.get("/consumables")
@login_required
@permission_required("stock.view")
def consumables():
    rows = Consumable.query.filter_by(is_active=True).order_by(Consumable.name).all()
    return render_template("c4/consumables.html", consumables=rows)


@c4_bp.get("/consumables/<int:consumable_id>")
@login_required
@permission_required("stock.view")
def consumable_detail(consumable_id):
    consumable = Consumable.query.get_or_404(consumable_id)
    usages = consumable.usages.order_by(ConsumableUsage.used_at.desc()).limit(100).all()
    orders = ConsumableOrder.query.filter_by(consumable_id=consumable.id).order_by(ConsumableOrder.id.desc()).all()
    compatible = (EquipmentConsumable.query.filter_by(consumable_id=consumable.id)
                  .options(joinedload(EquipmentConsumable.equipment)).all())
    coverage = estimate_consumable_stock_coverage(consumable=consumable)
    quantity_on_order = sum(order.remaining_quantity for order in orders if order.status in {"ORDERED", "PARTIALLY_RECEIVED"})
    return render_template("c4/consumable_detail.html", consumable=consumable, usages=usages, orders=orders,
                           compatible=compatible, coverage=coverage, quantity_on_order=quantity_on_order)


@c4_bp.post("/consumables/<int:consumable_id>/orders")
@login_required
@permission_required("stock.manage")
def order_consumable(consumable_id):
    consumable = Consumable.query.get_or_404(consumable_id)
    try:
        create_consumable_order(consumable=consumable, quantity=request.form.get("quantity", type=int), supplier_id=request.form.get("supplier_id", type=int), user_id=current_user.id)
        flash("Commande créée.", "success")
    except (ValueError, C4DomainError) as exc:
        db.session.rollback()
        flash(str(exc), "danger")
    return redirect(url_for("c4.consumable_detail", consumable_id=consumable_id))


@c4_bp.post("/orders/<int:order_id>/receive")
@login_required
@permission_required("stock.receive")
def receive_order(order_id):
    order = ConsumableOrder.query.get_or_404(order_id)
    try:
        receive_consumable_order(order=order, quantity=request.form.get("quantity", type=int), user_id=current_user.id, comment=request.form.get("comment"))
        flash("Réception enregistrée ; le stock a été augmenté.", "success")
    except (ValueError, C4DomainError) as exc:
        db.session.rollback()
        flash(str(exc), "danger")
    return redirect(url_for("c4.consumable_detail", consumable_id=order.consumable_id))


@c4_bp.post("/orders/<int:order_id>/cancel")
@login_required
@permission_required("stock.manage")
def cancel_order(order_id):
    order = ConsumableOrder.query.get_or_404(order_id)
    try:
        cancel_consumable_order(order=order, user_id=current_user.id, reason=request.form.get("reason"))
        flash("Commande annulée ; le stock déjà reçu est conservé.", "success")
    except (ValueError, C4DomainError) as exc:
        db.session.rollback()
        flash(str(exc), "danger")
    return redirect(url_for("c4.consumable_detail", consumable_id=order.consumable_id))


@c4_bp.post("/consumables/<int:consumable_id>/issue")
@login_required
@permission_required("stock.issue")
def issue_consumable(consumable_id):
    consumable = Consumable.query.get_or_404(consumable_id)
    equipment = Equipment.query.get_or_404(request.form.get("equipment_id", type=int))
    try:
        record_consumable_issue(consumable=consumable, equipment=equipment, quantity=request.form.get("quantity", type=int, default=1), user_id=current_user.id, notes=request.form.get("notes"))
        flash("Sortie de stock enregistrée.", "success")
    except (ValueError, C4DomainError) as exc:
        db.session.rollback()
        flash(str(exc), "danger")
    return redirect(url_for("c4.consumable_detail", consumable_id=consumable_id))
