from flask import Blueprint, jsonify, request

from hiveos.api.context import (
    authorize_core_action,
    build_action_run_id,
    emit_action_applied,
    emit_action_failed,
    get_core_from_request,
)
from hiveos.intelligence.evolution_policy import evaluate_candidates, select_winner
from hiveos.intelligence.tracing import build_trace_event, emit_trace


proposals_bp = Blueprint("proposals", __name__)


def _as_bool(value, default=False):
    if value is None:
        return default
    text = str(value).strip().lower()
    if text in {"1", "true", "yes", "on"}:
        return True
    if text in {"0", "false", "no", "off"}:
        return False
    return default


@proposals_bp.route("/proposals", methods=["GET"])
def list_proposals():
    try:
        core = get_core_from_request()
    except PermissionError as e:
        return jsonify({"error": str(e)}), 403
    task_id = request.args.get("task_id", default=None, type=int)
    include_archived = _as_bool(request.args.get("include_archived"), default=False)
    proposals = core.proposal_manager.list(task_id=task_id, include_archived=include_archived)
    return jsonify({"proposals": proposals})


@proposals_bp.route("/proposal_artifacts", methods=["GET"])
def proposal_artifacts():
    try:
        core = get_core_from_request()
    except PermissionError as e:
        return jsonify({"error": str(e)}), 403

    proposal_id = request.args.get("proposal_id")
    task_id = request.args.get("task_id", default=None, type=int)
    include_archived = _as_bool(request.args.get("include_archived"), default=False)

    if proposal_id:
        proposal = core.proposal_manager.get(proposal_id)
        if not proposal:
            return jsonify({"error": "proposal_not_found"}), 404
        return jsonify({"proposal": proposal})

    proposals = core.proposal_manager.list(task_id=task_id, include_archived=include_archived)
    return jsonify({"proposals": proposals})


@proposals_bp.route("/inject_proposal", methods=["POST"])
def inject_proposal():
    action = "proposals.inject"
    data = request.get_json() or {}
    proposal_id = data.get("proposal_id")
    if not proposal_id:
        return jsonify({"error": "Missing proposal_id"}), 400
    try:
        core = authorize_core_action(action, resource_type="proposal", resource_id=proposal_id)
    except PermissionError as e:
        return jsonify({"error": str(e)}), 403
    try:
        injected = core.proposal_manager.inject_validated(proposal_id, core)
        emit_action_applied(
            action,
            resource_type="proposal",
            resource_id=proposal_id,
            payload={
                "child_task_id": injected.get("child_task_id"),
                "parent_task_id": injected.get("parent_task_id"),
            },
        )
        return (
            jsonify(
                {
                    "status": "injected",
                    "proposal_id": proposal_id,
                    "child_task_id": injected.get("child_task_id"),
                    "parent_task_id": injected.get("parent_task_id"),
                }
            ),
            200,
        )
    except Exception as exc:
        emit_action_failed(action, resource_type="proposal", resource_id=proposal_id, reason=exc)
        return jsonify({"error": str(exc)}), 400


@proposals_bp.route("/validate_proposal", methods=["POST"])
def validate_proposal():
    action = "proposals.validate"
    data = request.get_json() or {}
    proposal_id = str(data.get("proposal_id") or "").strip()
    draft_artifact = data.get("proposal_artifact")
    actor = data.get("validation_actor")
    validation_mode = str(data.get("validation_mode") or "operator_requested").strip() or "operator_requested"
    validation_source = str(data.get("validation_source") or "workbench").strip() or "workbench"

    if not proposal_id and not isinstance(draft_artifact, dict):
        return jsonify({"error": "Missing proposal_id or proposal_artifact"}), 400

    try:
        core = authorize_core_action(action, resource_type="proposal", resource_id=(proposal_id or "draft"))
    except PermissionError as e:
        return jsonify({"error": str(e)}), 403

    session_id = request.headers.get("X-Session-ID")
    user_id = request.headers.get("X-User-ID")
    validation_actor = str(actor or (f"user:{user_id}" if user_id else "operator")).strip() or "operator"

    try:
        if not proposal_id:
            created = core.proposal_manager.create(
                agent_id=validation_actor,
                run_id=build_action_run_id(action, user_id=user_id, session_id=session_id),
                task_id=data.get("task_id"),
                chunk_id=str(data.get("chunk_id") or "operator_validation"),
                proposal_content=draft_artifact,
            )
            proposal_id = created.get("proposal_id")

        validation = core.proposal_manager.validate(
            proposal_id,
            validation_mode=validation_mode,
            validation_source=validation_source,
            validation_actor=validation_actor,
        )
        proposal = core.proposal_manager.get(proposal_id)
        outcome = "success" if bool(validation.get("ok")) else "denied"
        emit_trace(
            build_trace_event(
                event_type="proposal_validated",
                run_id=build_action_run_id(action, user_id=user_id, session_id=session_id),
                outcome=outcome,
                payload={
                    "proposal_id": proposal_id,
                    "validation": validation,
                    "validation_mode": validation_mode,
                    "validation_source": validation_source,
                    "validation_actor": validation_actor,
                },
                task_id=(proposal or {}).get("task_id"),
                chunk_id=(proposal or {}).get("chunk_id"),
            )
        )
        emit_action_applied(
            action,
            resource_type="proposal",
            resource_id=proposal_id,
            user_id=user_id,
            session_id=session_id,
            payload={
                "status": (proposal or {}).get("status"),
                "validation_mode": validation_mode,
                "validation_source": validation_source,
                "validation_actor": validation_actor,
                "validation_ok": bool(validation.get("ok")),
            },
        )
        return jsonify({"proposal_id": proposal_id, "proposal": proposal, "validation": validation}), 200
    except Exception as exc:
        emit_action_failed(
            action,
            resource_type="proposal",
            resource_id=(proposal_id or "draft"),
            reason=exc,
            user_id=user_id,
            session_id=session_id,
            payload={
                "validation_mode": validation_mode,
                "validation_source": validation_source,
                "validation_actor": validation_actor,
            },
        )
        return jsonify({"error": str(exc)}), 400


@proposals_bp.route("/archive_proposal", methods=["POST"])
def archive_proposal():
    action = "proposals.archive"
    data = request.get_json() or {}
    proposal_id = str(data.get("proposal_id") or "").strip()
    older_than_seconds = data.get("older_than_seconds")
    statuses = data.get("statuses")
    actor = data.get("archive_actor")
    archive_mode = str(data.get("archive_mode") or ("operator_bulk" if not proposal_id else "operator_requested")).strip()
    archive_source = str(data.get("archive_source") or "workbench").strip() or "workbench"

    if not proposal_id and older_than_seconds is None:
        return jsonify({"error": "Missing proposal_id or older_than_seconds"}), 400
    if statuses is not None and not isinstance(statuses, list):
        return jsonify({"error": "statuses must be a list"}), 400

    try:
        core = authorize_core_action(action, resource_type="proposal", resource_id=(proposal_id or "bulk"))
    except PermissionError as e:
        return jsonify({"error": str(e)}), 403

    session_id = request.headers.get("X-Session-ID")
    user_id = request.headers.get("X-User-ID")
    archive_actor = str(actor or (f"user:{user_id}" if user_id else "operator")).strip() or "operator"

    try:
        if proposal_id:
            archived = core.proposal_manager.archive(
                proposal_id,
                archive_mode=archive_mode,
                archive_source=archive_source,
                archive_actor=archive_actor,
            )
            if not archived:
                return jsonify({"error": "proposal_not_found"}), 404
            payload = {
                "proposal_id": proposal_id,
                "archived_count": 1,
                "archived_ids": [proposal_id],
                "archive_mode": archive_mode,
                "archive_source": archive_source,
                "archive_actor": archive_actor,
            }
            emit_trace(
                build_trace_event(
                    event_type="proposal_archived",
                    run_id=build_action_run_id(action, user_id=user_id, session_id=session_id),
                    outcome="success",
                    payload=payload,
                    task_id=(archived or {}).get("task_id"),
                    chunk_id=(archived or {}).get("chunk_id"),
                )
            )
            emit_action_applied(
                action,
                resource_type="proposal",
                resource_id=proposal_id,
                user_id=user_id,
                session_id=session_id,
                payload=payload,
            )
            return jsonify(payload), 200

        try:
            older_than_seconds_int = max(0, int(older_than_seconds))
        except (TypeError, ValueError):
            return jsonify({"error": "older_than_seconds must be numeric"}), 400
        archived_ids = core.proposal_manager.archive_older_than(
            older_than_seconds=older_than_seconds_int,
            statuses=statuses or ["created", "rejected", "injected"],
            archive_mode=archive_mode,
            archive_source=archive_source,
            archive_actor=archive_actor,
        )
        payload = {
            "archived_count": len(archived_ids),
            "archived_ids": archived_ids,
            "archive_mode": archive_mode,
            "archive_source": archive_source,
            "archive_actor": archive_actor,
            "older_than_seconds": older_than_seconds_int,
            "statuses": statuses or ["created", "rejected", "injected"],
        }
        emit_trace(
            build_trace_event(
                event_type="proposal_archived",
                run_id=build_action_run_id(action, user_id=user_id, session_id=session_id),
                outcome="success",
                payload=payload,
            )
        )
        emit_action_applied(
            action,
            resource_type="proposal",
            resource_id="bulk",
            user_id=user_id,
            session_id=session_id,
            payload=payload,
        )
        return jsonify(payload), 200
    except Exception as exc:
        emit_action_failed(
            action,
            resource_type="proposal",
            resource_id=(proposal_id or "bulk"),
            reason=exc,
            user_id=user_id,
            session_id=session_id,
            payload={
                "archive_mode": archive_mode,
                "archive_source": archive_source,
                "archive_actor": archive_actor,
            },
        )
        return jsonify({"error": str(exc)}), 400


@proposals_bp.route("/rank_proposals", methods=["POST"])
def rank_proposals():
    try:
        core = get_core_from_request()
    except PermissionError as e:
        return jsonify({"error": str(e)}), 403

    data = request.get_json() or {}
    task_id = data.get("task_id")
    proposal_ids = data.get("proposal_ids")
    policy = data.get("policy") or {}
    require_validated = bool(data.get("require_validated", True))

    if task_id is not None:
        try:
            task_id = int(task_id)
        except (TypeError, ValueError):
            return jsonify({"error": "task_id must be numeric"}), 400

    candidates = core.proposal_manager.list(task_id=task_id)
    if proposal_ids is not None:
        if not isinstance(proposal_ids, list):
            return jsonify({"error": "proposal_ids must be a list"}), 400
        allowed_ids = {str(item) for item in proposal_ids}
        candidates = [
            proposal
            for proposal in candidates
            if str(proposal.get("proposal_id")) in allowed_ids
        ]

    ranked = evaluate_candidates(candidates, policy=policy)
    winner = select_winner(ranked, require_validated=require_validated)
    return jsonify(
        {
            "count": len(ranked),
            "ranked": ranked,
            "winner": winner,
            "require_validated": require_validated,
        }
    )
