"""
API v1 - Time Entries and Timer endpoints.
Sub-blueprint for /api/v1/time-entries and /api/v1/timer/*.
"""

from flask import Blueprint, g, jsonify, request
from marshmallow import ValidationError

from app.routes.api_v1_common import _parse_date_range, paginate_query, parse_datetime
from app.schemas.time_entry_schema import TimeEntryCreateSchema, TimeEntryUpdateSchema
from app.utils.api_auth import require_api_token
from app.utils.api_responses import (
    error_response,
    forbidden_response,
    handle_validation_error,
    validation_error_response,
)

api_v1_time_entries_bp = Blueprint("api_v1_time_entries", __name__, url_prefix="/api/v1")


@api_v1_time_entries_bp.route("/time-entries", methods=["GET"])
@require_api_token("read:time_entries")
def list_time_entries():
    """List time entries with filters."""
    from sqlalchemy.orm import joinedload

    from app.models import TimeEntry

    project_id = request.args.get("project_id", type=int)
    user_id = request.args.get("user_id", type=int)
    if user_id:
        if not g.api_user.is_admin and user_id != g.api_user.id:
            return forbidden_response("Access denied")
    else:
        if not g.api_user.is_admin:
            user_id = g.api_user.id

    start_date = request.args.get("start_date")
    end_date = request.args.get("end_date")
    start_dt, end_dt = _parse_date_range(start_date, end_date)

    billable = request.args.get("billable")
    billable_filter = None
    if billable is not None:
        billable_filter = billable.lower() == "true"

    include_active = request.args.get("include_active") == "true"
    page = request.args.get("page", 1, type=int)
    per_page = request.args.get("per_page", 50, type=int)

    query = TimeEntry.query.options(
        joinedload(TimeEntry.project), joinedload(TimeEntry.user), joinedload(TimeEntry.task)
    )
    if project_id:
        query = query.filter(TimeEntry.project_id == project_id)
    if user_id:
        query = query.filter(TimeEntry.user_id == user_id)
    if start_dt:
        query = query.filter(TimeEntry.start_time >= start_dt)
    if end_dt:
        query = query.filter(TimeEntry.start_time <= end_dt)
    if billable_filter is not None:
        query = query.filter(TimeEntry.billable == billable_filter)
    if not include_active:
        query = query.filter(TimeEntry.end_time.isnot(None))

    query = query.order_by(TimeEntry.start_time.desc())
    result = paginate_query(query, page, per_page)
    return jsonify({"time_entries": [e.to_dict() for e in result["items"]], "pagination": result["pagination"]})


@api_v1_time_entries_bp.route("/time-entries/<int:entry_id>", methods=["GET"])
@require_api_token("read:time_entries")
def get_time_entry(entry_id):
    """Get a specific time entry."""
    from sqlalchemy.orm import joinedload

    from app.models import TimeEntry

    entry = (
        TimeEntry.query.options(joinedload(TimeEntry.project), joinedload(TimeEntry.user), joinedload(TimeEntry.task))
        .filter_by(id=entry_id)
        .first_or_404()
    )
    if not g.api_user.is_admin and entry.user_id != g.api_user.id:
        return forbidden_response("Access denied")
    return jsonify({"time_entry": entry.to_dict()})


@api_v1_time_entries_bp.route("/time-entries/import-csv", methods=["POST"])
@require_api_token("write:time_entries")
def import_time_entries_csv():
    """Import time entries from CSV (header row required)."""
    from app.services.time_entry_csv_import_service import import_time_entries_from_csv_text

    csv_text = ""
    if request.files and request.files.get("file"):
        up = request.files["file"]
        csv_text = (up.read() or b"").decode("utf-8", errors="replace")
    elif request.is_json:
        data = request.get_json() or {}
        csv_text = (data.get("csv") or data.get("data") or "") or ""
    else:
        csv_text = request.get_data(as_text=True) or ""

    result, status = import_time_entries_from_csv_text(
        csv_text, user_id=g.api_user.id, is_admin=g.api_user.is_admin
    )
    return jsonify(result), status


@api_v1_time_entries_bp.route("/time-entries/bulk", methods=["POST"])
@require_api_token("write:time_entries")
def bulk_time_entries():
    """Bulk actions on time entries (same behavior as session /api/entries/bulk)."""
    from app.services.time_entry_bulk_service import apply_bulk_time_entry_actions

    data = request.get_json() or {}
    entry_ids = data.get("entry_ids") or []
    action = (data.get("action") or "").strip()
    value = data.get("value")
    if not entry_ids or not isinstance(entry_ids, list):
        return validation_error_response(
            errors={"entry_ids": ["Must be a non-empty list of integer ids"]},
            message="Invalid entry_ids",
        )
    ids = []
    for eid in entry_ids:
        try:
            ids.append(int(eid))
        except (TypeError, ValueError):
            return validation_error_response(errors={"entry_ids": ["All entry ids must be integers"]})
    result = apply_bulk_time_entry_actions(
        ids, action, value, user_id=g.api_user.id, is_admin=g.api_user.is_admin
    )
    if not result.get("success"):
        code = result.get("http_status", 400)
        return error_response(result.get("error", "Bulk operation failed"), status_code=code)
    return jsonify({"success": True, "affected": result.get("affected", 0)})


@api_v1_time_entries_bp.route("/time-entries", methods=["POST"])
@require_api_token("write:time_entries")
def create_time_entry():
    """Create a new time entry."""
    from app.services import TimeTrackingService

    from app.utils.api_idempotency import (
        SCOPE_POST_TIME_ENTRY,
        lookup_idempotent_response,
        normalize_idempotency_key,
        replay_response,
        store_idempotent_response,
    )

    idem_key = normalize_idempotency_key(request.headers.get("Idempotency-Key"))
    if idem_key:
        existing = lookup_idempotent_response(g.api_token.id, SCOPE_POST_TIME_ENTRY, idem_key)
        if existing:
            status_code, body_json = existing
            return replay_response(status_code, body_json)

    data = request.get_json() or {}
    schema = TimeEntryCreateSchema()
    try:
        validated = schema.load(data)
    except ValidationError as err:
        return handle_validation_error(err)

    start_time = validated["start_time"]
    end_time = validated.get("end_time") or start_time

    time_tracking_service = TimeTrackingService()
    result = time_tracking_service.create_manual_entry(
        user_id=g.api_user.id,
        project_id=validated.get("project_id"),
        client_id=validated.get("client_id"),
        start_time=start_time,
        end_time=end_time,
        break_seconds=validated.get("break_seconds"),
        task_id=validated.get("task_id"),
        notes=validated.get("notes"),
        tags=validated.get("tags"),
        billable=validated.get("billable", True),
        paid=validated.get("paid", False),
        invoice_number=validated.get("invoice_number"),
    )

    if not result.get("success"):
        return error_response(
            result.get("message", "Could not create time entry"),
            status_code=400,
        )

    entry = result.get("entry")
    if entry:
        from app.models import Activity
        from app.utils.audit import get_request_info

        entity_name = entry.project.name if entry.project else (entry.client.name if entry.client else "Unknown")
        task_name = entry.task.name if entry.task else None
        duration_formatted = entry.duration_formatted if hasattr(entry, "duration_formatted") else "0:00"
        ip_address, user_agent, _ = get_request_info()
        Activity.log(
            user_id=g.api_user.id,
            action="created",
            entity_type="time_entry",
            entity_id=entry.id,
            entity_name=f"{entity_name}" + (f" - {task_name}" if task_name else ""),
            description=f"Created time entry for {entity_name}"
            + (f" - {task_name}" if task_name else "")
            + f" - {duration_formatted}",
            extra_data={
                "project_name": entry.project.name if entry.project else None,
                "client_name": entry.client.name if entry.client else None,
                "task_name": task_name,
                "duration_formatted": duration_formatted,
                "duration_hours": entry.duration_hours if hasattr(entry, "duration_hours") else None,
            },
            ip_address=ip_address,
            user_agent=user_agent,
        )

    payload = {"message": "Time entry created successfully", "time_entry": result["entry"].to_dict()}
    resp = jsonify(payload)
    resp.status_code = 201
    if idem_key:
        from sqlalchemy.exc import IntegrityError

        from app import db

        try:
            store_idempotent_response(g.api_token.id, SCOPE_POST_TIME_ENTRY, idem_key, 201, payload)
        except IntegrityError:
            db.session.rollback()
            existing = lookup_idempotent_response(g.api_token.id, SCOPE_POST_TIME_ENTRY, idem_key)
            if existing:
                status_code, body_json = existing
                return replay_response(status_code, body_json)
    return resp


@api_v1_time_entries_bp.route("/time-entries/<int:entry_id>", methods=["PUT", "PATCH"])
@require_api_token("write:time_entries")
def update_time_entry(entry_id):
    """Update a time entry."""
    from app.services import TimeTrackingService

    data = request.get_json() or {}
    schema = TimeEntryUpdateSchema()
    try:
        validated = schema.load(data, partial=True)
    except ValidationError as err:
        return handle_validation_error(err)

    time_tracking_service = TimeTrackingService()
    result = time_tracking_service.update_entry(
        entry_id=entry_id,
        user_id=g.api_user.id,
        is_admin=g.api_user.is_admin,
        project_id=validated.get("project_id"),
        client_id=validated.get("client_id"),
        task_id=validated.get("task_id"),
        start_time=validated.get("start_time"),
        end_time=validated.get("end_time"),
        break_seconds=validated.get("break_seconds"),
        notes=validated.get("notes"),
        tags=validated.get("tags"),
        billable=validated.get("billable"),
        paid=validated.get("paid"),
        invoice_number=validated.get("invoice_number"),
        reason=data.get("reason"),
        expected_updated_at=validated.get("if_updated_at"),
    )

    if not result.get("success"):
        if result.get("error") == "conflict":
            return error_response(
                result.get("message", "Conflict"),
                error_code="conflict",
                status_code=409,
            )
        return error_response(
            result.get("message", "Could not update time entry"),
            status_code=400,
        )

    entry = result.get("entry")
    if entry:
        from app.models import Activity
        from app.utils.audit import get_request_info

        entity_name = entry.project.name if entry.project else (entry.client.name if entry.client else "Unknown")
        task_name = entry.task.name if entry.task else None
        ip_address, user_agent, _ = get_request_info()
        Activity.log(
            user_id=g.api_user.id,
            action="updated",
            entity_type="time_entry",
            entity_id=entry.id,
            entity_name=f"{entity_name}" + (f" - {task_name}" if task_name else ""),
            description=f"Updated time entry for {entity_name}" + (f" - {task_name}" if task_name else ""),
            extra_data={
                "project_name": entry.project.name if entry.project else None,
                "client_name": entry.client.name if entry.client else None,
                "task_name": task_name,
            },
            ip_address=ip_address,
            user_agent=user_agent,
        )

    return jsonify({"message": "Time entry updated successfully", "time_entry": result["entry"].to_dict()})


@api_v1_time_entries_bp.route("/time-entries/<int:entry_id>", methods=["DELETE"])
@require_api_token("write:time_entries")
def delete_time_entry(entry_id):
    """Delete a time entry."""
    from app.services import TimeTrackingService

    data = request.get_json() or {}
    reason = data.get("reason")
    time_tracking_service = TimeTrackingService()
    result = time_tracking_service.delete_entry(
        entry_id=entry_id,
        user_id=g.api_user.id,
        is_admin=g.api_user.is_admin,
        reason=reason,
    )
    if not result.get("success"):
        return error_response(
            result.get("message", "Could not delete time entry"),
            status_code=400,
        )
    return jsonify({"message": "Time entry deleted successfully"})


@api_v1_time_entries_bp.route("/timer/status", methods=["GET"])
@require_api_token("read:time_entries")
def timer_status():
    """Get current timer status."""
    active_timer = g.api_user.active_timer
    if not active_timer:
        return jsonify({"active": False, "timer": None})
    return jsonify({"active": True, "timer": active_timer.to_dict()})


@api_v1_time_entries_bp.route("/timer/start", methods=["POST"])
@require_api_token("write:time_entries")
def start_timer():
    """Start a new timer."""
    from app.services import TimeTrackingService

    data = request.get_json() or {}
    project_id = data.get("project_id")
    if not project_id:
        return validation_error_response(
            errors={"project_id": ["project_id is required"]},
            message="project_id is required",
        )

    from app.utils.scope_filter import user_can_access_project

    if not user_can_access_project(g.api_user, project_id):
        return forbidden_response("You do not have access to this project")

    time_tracking_service = TimeTrackingService()
    result = time_tracking_service.start_timer(
        user_id=g.api_user.id,
        project_id=project_id,
        task_id=data.get("task_id"),
        notes=data.get("notes"),
        template_id=data.get("template_id"),
    )
    if not result.get("success"):
        if result.get("error") == "timer_already_running":
            return error_response(
                result.get("message", "Could not start timer"),
                error_code="timer_already_running",
                status_code=409,
            )
        return error_response(
            result.get("message", "Could not start timer"),
            status_code=400,
        )
    return jsonify({"message": "Timer started successfully", "timer": result["timer"].to_dict()}), 201


@api_v1_time_entries_bp.route("/timer/pause", methods=["POST"])
@require_api_token("write:time_entries")
def pause_timer():
    """Pause the active timer (clock stops; break accumulates on resume)."""
    from app.services import TimeTrackingService

    time_tracking_service = TimeTrackingService()
    result = time_tracking_service.pause_timer(user_id=g.api_user.id)
    if not result.get("success"):
        return error_response(
            result.get("message", "Could not pause timer"),
            error_code=result.get("error", "pause_failed"),
            status_code=400,
        )
    return jsonify({"message": "Timer paused", "time_entry": result["entry"].to_dict()})


@api_v1_time_entries_bp.route("/timer/resume", methods=["POST"])
@require_api_token("write:time_entries")
def resume_timer():
    """Resume a paused timer (time since pause is counted as break)."""
    from app.services import TimeTrackingService

    time_tracking_service = TimeTrackingService()
    result = time_tracking_service.resume_timer(user_id=g.api_user.id)
    if not result.get("success"):
        return error_response(
            result.get("message", "Could not resume timer"),
            error_code=result.get("error", "resume_failed"),
            status_code=400,
        )
    return jsonify({"message": "Timer resumed", "time_entry": result["entry"].to_dict()})


@api_v1_time_entries_bp.route("/timer/stop", methods=["POST"])
@require_api_token("write:time_entries")
def stop_timer():
    """Stop the active timer."""
    from app.services import TimeTrackingService

    active_timer = g.api_user.active_timer
    if not active_timer:
        return error_response(
            "No active timer to stop",
            error_code="no_active_timer",
            status_code=400,
        )
    time_tracking_service = TimeTrackingService()
    result = time_tracking_service.stop_timer(user_id=g.api_user.id, entry_id=active_timer.id)
    if not result.get("success"):
        return error_response(
            result.get("message", "Could not stop timer"),
            error_code=result.get("error", "stop_failed"),
            status_code=400,
        )
    return jsonify({"message": "Timer stopped successfully", "time_entry": result["entry"].to_dict()})
