import json
import os
import tempfile
from pathlib import Path

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from django.views.decorators.http import require_http_methods


DEFAULT_TIMEOUT_MINUTES = 15
MIN_TIMEOUT_MINUTES = 1
MAX_TIMEOUT_MINUTES = 480


def _is_admin(user):
    return (
        user.is_authenticated
        and user.is_active
        and user.is_staff
        and user.is_superuser
    )


def _config_path():
    return Path(settings.BASE_DIR) / "session_timeout.json"


def _read_timeout():
    try:
        with _config_path().open("r", encoding="utf-8") as file:
            data = json.load(file)

        minutes = int(data.get("minutes", DEFAULT_TIMEOUT_MINUTES))
        if MIN_TIMEOUT_MINUTES <= minutes <= MAX_TIMEOUT_MINUTES:
            return minutes
    except (OSError, ValueError, TypeError, json.JSONDecodeError):
        pass

    return DEFAULT_TIMEOUT_MINUTES


def _save_timeout(minutes):
    target = _config_path()
    temp_path = None

    try:
        with tempfile.NamedTemporaryFile(
            mode="w",
            encoding="utf-8",
            dir=str(target.parent),
            prefix=".session_timeout_",
            suffix=".tmp",
            delete=False,
        ) as file:
            temp_path = file.name
            json.dump({"minutes": minutes}, file, indent=2)
            file.write("\n")
            file.flush()
            os.fsync(file.fileno())

        os.replace(temp_path, target)
        return True

    except OSError:
        if temp_path:
            try:
                os.unlink(temp_path)
            except OSError:
                pass
        return False


@login_required
@require_http_methods(["GET", "POST"])
def session_settings(request):
    if not _is_admin(request.user):
        messages.error(
            request,
            "Access denied. Only an administrator can change session security settings.",
        )
        return redirect("dashboard")

    if request.method == "POST":
        raw_value = request.POST.get("minutes", "").strip()

        if not raw_value.isdigit():
            messages.error(request, "Enter a whole number of minutes.")
            return render(
                request,
                "accounts/session_settings.html",
                {
                    "minutes": _read_timeout(),
                    "min_minutes": MIN_TIMEOUT_MINUTES,
                    "max_minutes": MAX_TIMEOUT_MINUTES,
                },
            )

        minutes = int(raw_value)

        if not MIN_TIMEOUT_MINUTES <= minutes <= MAX_TIMEOUT_MINUTES:
            messages.error(
                request,
                f"Session timeout must be between {MIN_TIMEOUT_MINUTES} "
                f"and {MAX_TIMEOUT_MINUTES} minutes.",
            )
        elif _save_timeout(minutes):
            messages.success(
                request,
                f"Session timeout setting saved as {minutes} minutes.",
            )
        else:
            messages.error(
                request,
                "Could not save the setting. Check file permissions and try again.",
            )

        return redirect("session_settings")

    return render(
        request,
        "accounts/session_settings.html",
        {
            "minutes": _read_timeout(),
            "min_minutes": MIN_TIMEOUT_MINUTES,
            "max_minutes": MAX_TIMEOUT_MINUTES,
        },
    )
