"""User profile and settings routes"""

import hmac
import re
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError

from flask import Blueprint, current_app, flash, jsonify, redirect, render_template, request, url_for
from flask_babel import gettext as _
from flask_login import current_user, login_required

from app import db
from app.models import Activity, Settings, User
from app.utils.db import safe_commit
from app.utils.donate_hide_code import compute_donate_hide_code, verify_ed25519_signature
from app.utils.license_utils import is_license_activated
from app.utils.timezone import get_available_timezones

HEX_COLOR_RE = re.compile(r"^#[0-9A-Fa-f]{6}$")

user_bp = Blueprint("user", __name__)


@user_bp.route("/profile")
@login_required
def profile():
    """User profile page"""
    # Get user statistics
    total_hours = current_user.total_hours
    active_timer = current_user.active_timer
    recent_entries = current_user.get_recent_entries(limit=10)

    # Get recent activities
    recent_activities = Activity.get_recent(user_id=current_user.id, limit=20)

    return render_template(
        "user/profile.html",
        user=current_user,
        total_hours=total_hours,
        active_timer=active_timer,
        recent_entries=recent_entries,
        recent_activities=recent_activities,
    )


@user_bp.route("/settings", methods=["GET", "POST"])
@login_required
def settings():
    """User settings and preferences page"""
    if request.method == "POST":
        try:
            # Notification preferences
            current_user.email_notifications = "email_notifications" in request.form
            current_user.notification_overdue_invoices = "notification_overdue_invoices" in request.form
            current_user.notification_task_assigned = "notification_task_assigned" in request.form
            current_user.notification_task_comments = "notification_task_comments" in request.form
            current_user.notification_weekly_summary = "notification_weekly_summary" in request.form
            current_user.notification_remind_to_log = "notification_remind_to_log" in request.form
            reminder_time = request.form.get("reminder_to_log_time", "").strip()
            if reminder_time and len(reminder_time) <= 5:
                import re

                if re.match(r"^([01]?\d|2[0-3]):[0-5]\d$", reminder_time):
                    current_user.reminder_to_log_time = reminder_time
                else:
                    current_user.reminder_to_log_time = None
            else:
                current_user.reminder_to_log_time = None

            # Smart in-app notifications (separate from email remind-to-log)
            current_user.smart_notifications_enabled = "smart_notifications_enabled" in request.form
            current_user.smart_notify_no_tracking = "smart_notify_no_tracking" in request.form
            current_user.smart_notify_long_timer = "smart_notify_long_timer" in request.form
            current_user.smart_notify_daily_summary = "smart_notify_daily_summary" in request.form
            current_user.smart_notify_browser = "smart_notify_browser" in request.form
            for form_key, attr in (
                ("smart_notify_no_tracking_after", "smart_notify_no_tracking_after"),
                ("smart_notify_summary_at", "smart_notify_summary_at"),
            ):
                raw = (request.form.get(form_key) or "").strip()
                if raw and len(raw) <= 5:
                    if re.match(r"^([01]?\d|2[0-3]):[0-5]\d$", raw):
                        setattr(current_user, attr, raw)
                    else:
                        setattr(current_user, attr, None)
                else:
                    setattr(current_user, attr, None)

            # Profile information
            full_name = request.form.get("full_name", "").strip()
            if full_name:
                current_user.full_name = full_name

            email = request.form.get("email", "").strip()
            if email:
                current_user.email = email

            # Display preferences
            theme_preference = request.form.get("theme_preference")
            if theme_preference in ["light", "dark", None, ""]:
                current_user.theme_preference = theme_preference if theme_preference else None

            # Regional settings
            timezone = request.form.get("timezone")
            if timezone is not None:
                timezone = timezone.strip()
                if timezone == "":
                    current_user.timezone = None
                else:
                    try:
                        # Validate timezone
                        ZoneInfo(timezone)
                        current_user.timezone = timezone
                    except (ZoneInfoNotFoundError, KeyError):
                        flash(_("Invalid timezone selected"), "error")
                        return redirect(url_for("user.settings"))

            date_format = request.form.get("date_format")
            if date_format is not None:
                allowed_date = {"YYYY-MM-DD", "MM/DD/YYYY", "DD/MM/YYYY", "DD.MM.YYYY"}
                current_user.date_format = date_format if date_format in allowed_date else None

            time_format = request.form.get("time_format")
            if time_format is not None:
                current_user.time_format = time_format if time_format in ("12h", "24h") else None

            week_start_day = request.form.get("week_start_day", type=int)
            if week_start_day is not None and 0 <= week_start_day <= 6:
                current_user.week_start_day = week_start_day

            # Calendar default view
            calendar_default_view = request.form.get("calendar_default_view") or None
            if calendar_default_view is not None and calendar_default_view not in ("day", "week", "month"):
                calendar_default_view = None
            current_user.calendar_default_view = calendar_default_view

            # Language preference
            preferred_language = request.form.get("preferred_language")
            if preferred_language is not None:  # Allow empty string to clear preference
                current_user.preferred_language = preferred_language if preferred_language else None
                # Also update session for immediate effect
                from flask import session

                if preferred_language:
                    session["preferred_language"] = preferred_language
                    session.permanent = True
                    session.modified = True
                else:
                    session.pop("preferred_language", None)
                    session.modified = True

            # Time rounding preferences
            current_user.time_rounding_enabled = "time_rounding_enabled" in request.form

            time_rounding_minutes = request.form.get("time_rounding_minutes", type=int)
            if time_rounding_minutes and time_rounding_minutes in [1, 5, 10, 15, 30, 60]:
                current_user.time_rounding_minutes = time_rounding_minutes

            time_rounding_method = request.form.get("time_rounding_method")
            if time_rounding_method in ["nearest", "up", "down"]:
                current_user.time_rounding_method = time_rounding_method

            # Overtime settings
            standard_hours_per_day = request.form.get("standard_hours_per_day", type=float)
            if standard_hours_per_day is not None:
                # Validate range (0.5 to 24 hours)
                if 0.5 <= standard_hours_per_day <= 24:
                    current_user.standard_hours_per_day = standard_hours_per_day
                else:
                    flash(_("Standard hours per day must be between 0.5 and 24"), "error")
                    return redirect(url_for("user.settings"))
            if hasattr(current_user, "overtime_include_weekends"):
                current_user.overtime_include_weekends = request.form.get("overtime_include_weekends") == "on"
            overtime_mode = request.form.get("overtime_calculation_mode")
            if overtime_mode in ("daily", "weekly"):
                current_user.overtime_calculation_mode = overtime_mode
            if hasattr(current_user, "standard_hours_per_week"):
                standard_hours_per_week = request.form.get("standard_hours_per_week", type=float)
                if standard_hours_per_week is not None:
                    if 1 <= standard_hours_per_week <= 168:
                        current_user.standard_hours_per_week = standard_hours_per_week
                    else:
                        flash(_("Standard hours per week must be between 1 and 168"), "error")
                        return redirect(url_for("user.settings"))
                elif getattr(current_user, "overtime_calculation_mode", "daily") == "weekly":
                    # Allow clearing to use derived default (daily * 5)
                    current_user.standard_hours_per_week = None

            # Save changes
            if safe_commit(db.session):
                # Log activity
                Activity.log(
                    user_id=current_user.id,
                    action="updated",
                    entity_type="user",
                    entity_id=current_user.id,
                    entity_name=current_user.username,
                    description="Updated user settings",
                )

                flash(_("Settings saved successfully"), "success")
            else:
                flash(_("Error saving settings"), "error")

        except Exception as e:
            flash(_("Error saving settings: %(error)s", error=str(e)), "error")
            db.session.rollback()

        return redirect(url_for("user.settings"))

    # Get all available timezones
    timezones = get_available_timezones()

    # Get available languages from config
    from flask import current_app

    languages = current_app.config.get(
        "LANGUAGES",
        {"en": "English", "nl": "Nederlands", "de": "Deutsch", "fr": "Français", "it": "Italiano", "fi": "Suomi"},
    )

    # Get time rounding options
    from app.utils.time_rounding import get_available_rounding_intervals, get_available_rounding_methods

    rounding_intervals = get_available_rounding_intervals()
    rounding_methods = get_available_rounding_methods()

    return render_template(
        "user/settings.html",
        user=current_user,
        timezones=timezones,
        languages=languages,
        rounding_intervals=rounding_intervals,
        rounding_methods=rounding_methods,
    )


@user_bp.route("/settings/license", methods=["GET", "POST"])
@login_required
def license():
    """License management: supporter key validation (sets donate_ui_hidden / supporter instance flag)."""
    settings_obj = Settings.get_settings()
    if request.method == "POST":
        if is_license_activated(settings_obj):
            flash(_("This instance is already licensed."), "info")
            return redirect(url_for("user.license"))
        code = (request.form.get("license_key") or request.form.get("code") or "").strip()
        system_id = Settings.get_system_instance_id()
        if not system_id:
            flash(_("Invalid code."), "error")
            return redirect(url_for("user.license"))
        valid = False
        public_key_pem = current_app.config.get("DONATE_HIDE_PUBLIC_KEY_PEM") or ""
        if public_key_pem:
            valid = verify_ed25519_signature(code, system_id, public_key_pem)
        if not valid:
            secret = current_app.config.get("DONATE_HIDE_UNLOCK_SECRET") or ""
            if secret:
                expected = compute_donate_hide_code(secret, system_id)
                valid = bool(expected and hmac.compare_digest(code, expected))
        if not valid:
            flash(_("Invalid code."), "error")
            return redirect(url_for("user.license"))
        settings_obj.donate_ui_hidden = True
        if safe_commit(db.session):
            flash(_("License activated. Thank you for supporting TimeTracker!"), "success")
        else:
            flash(_("Error saving settings."), "error")
        return redirect(url_for("user.license"))
    return render_template(
        "user/license.html",
        is_license_activated=is_license_activated(settings_obj),
    )


@user_bp.route("/settings/verify-donate-hide-code", methods=["POST"])
@login_required
def verify_donate_hide_code():
    """Verify code (Ed25519 signature or HMAC) and set ui_show_donate=False."""

    if not getattr(current_user, "ui_show_donate", True):
        return jsonify({"success": True})

    data = request.get_json() or {}
    code = (data.get("code") or "").strip()
    system_id = Settings.get_system_instance_id()

    if not system_id:
        return jsonify({"error": _("Invalid code.")}), 400

    valid = False
    public_key_pem = current_app.config.get("DONATE_HIDE_PUBLIC_KEY_PEM") or ""
    if public_key_pem:
        valid = verify_ed25519_signature(code, system_id, public_key_pem)
    if not valid:
        secret = current_app.config.get("DONATE_HIDE_UNLOCK_SECRET") or ""
        if secret:
            expected = compute_donate_hide_code(secret, system_id)
            valid = bool(expected and hmac.compare_digest(code, expected))

    if not valid:
        return jsonify({"error": _("Invalid code.")}), 400

    current_user.ui_show_donate = False
    if safe_commit(db.session):
        return jsonify({"success": True})
    return jsonify({"error": _("Error saving settings")}), 500


@user_bp.route("/api/preferences", methods=["PATCH"])
@login_required
def update_preferences():
    """API endpoint to update user preferences (for AJAX calls)"""
    try:
        data = request.get_json()

        if "theme_preference" in data:
            theme = data["theme_preference"]
            if theme in ["light", "dark", "system", None, ""]:
                current_user.theme_preference = theme if theme and theme != "system" else None

        if "email_notifications" in data:
            current_user.email_notifications = bool(data["email_notifications"])

        if "timezone" in data:
            tz_value = data["timezone"]
            if tz_value in [None, "", "system"]:
                current_user.timezone = None
            else:
                try:
                    ZoneInfo(tz_value)
                    current_user.timezone = tz_value
                except (ZoneInfoNotFoundError, KeyError):
                    return jsonify({"error": "Invalid timezone"}), 400

        for key, attr in (
            ("calendar_color_events", "calendar_color_events"),
            ("calendar_color_tasks", "calendar_color_tasks"),
            ("calendar_color_time_entries", "calendar_color_time_entries"),
        ):
            if key in data:
                val = data[key]
                if val is None or val == "":
                    setattr(current_user, attr, None)
                elif isinstance(val, str) and HEX_COLOR_RE.match(val):
                    setattr(current_user, attr, val)
                else:
                    return jsonify({"error": f"Invalid {key}: must be null or hex color (#RRGGBB)"}), 400

        if "calendar_default_view" in data:
            val = data["calendar_default_view"]
            if val is None or val == "":
                current_user.calendar_default_view = None
            elif val in ("day", "week", "month"):
                current_user.calendar_default_view = val
            else:
                return jsonify({"error": "Invalid calendar_default_view: must be day, week, month, or null"}), 400

        db.session.commit()

        return jsonify({"success": True, "message": _("Preferences updated")})

    except Exception as e:
        db.session.rollback()
        return jsonify({"error": str(e)}), 500


@user_bp.route("/api/theme", methods=["POST"])
@login_required
def set_theme():
    """Quick API endpoint to set theme (for theme switcher)"""
    try:
        data = request.get_json()
        theme = data.get("theme")

        if theme in ["light", "dark", "system", None, ""]:
            current_user.theme_preference = None if (theme == "system" or not theme) else theme
            db.session.commit()

            return jsonify({"success": True, "theme": current_user.theme_preference or "system"})

        return jsonify({"error": "Invalid theme"}), 400

    except Exception as e:
        db.session.rollback()
        return jsonify({"error": str(e)}), 500


@user_bp.route("/api/language", methods=["POST"])
@login_required
def set_language():
    """Quick API endpoint to set language (for language switcher)"""
    from flask import current_app, session

    try:
        data = request.get_json()
        language = data.get("language")

        # Get available languages from config
        available_languages = current_app.config.get("LANGUAGES", {})

        if language in available_languages:
            # Update user preference
            current_user.preferred_language = language
            db.session.commit()

            # Also set in session for immediate effect
            session["preferred_language"] = language

            return jsonify({"success": True, "language": language, "message": _("Language updated successfully")})

        return jsonify({"error": _("Invalid language")}), 400

    except Exception as e:
        db.session.rollback()
        return jsonify({"error": str(e)}), 500


@user_bp.route("/set-language/<language>")
def set_language_direct(language):
    """Direct route to set language (for non-JS fallback)"""
    from flask import current_app, session

    # Get available languages from config
    available_languages = current_app.config.get("LANGUAGES", {})

    if language in available_languages:
        # Set in session for immediate effect
        session["preferred_language"] = language

        # If user is logged in, update their preference
        if current_user.is_authenticated:
            current_user.preferred_language = language
            db.session.commit()
            flash(_("Language updated to %(language)s", language=available_languages[language]), "success")

        # Redirect back to referring page or dashboard
        next_page = request.referrer or url_for("main.dashboard")
        return redirect(next_page)

    flash(_("Invalid language"), "error")
    return redirect(url_for("main.dashboard"))
