import logging
import os
from datetime import timedelta
from logging.handlers import RotatingFileHandler
from pathlib import Path

from flask import Flask, current_app, request
from flask_login import current_user, logout_user
from werkzeug.middleware.proxy_fix import ProxyFix

from config import INSTANCE_DIR, config_map

from .context_processors import register_context_processors
from .error_handlers import register_error_handlers
from .extensions import csrf, db, limiter, login_manager, migrate
from .services.security_service import SecurityService
from .services.localization_service import LocalizationService
from .services.settings_service import SettingsService
from .utils.helpers import format_bytes


def create_app(config_name: str | None = None) -> Flask:
    app = Flask(
        __name__,
        instance_path=str(INSTANCE_DIR),
        instance_relative_config=True,
        template_folder="templates",
        static_folder="static",
    )
    app.config.from_object(config_map.get(config_name or "development", config_map["development"]))

    _ensure_directories(app)
    _configure_logging(app)
    _configure_runtime_environment(app)
    _apply_proxy_fix(app)
    _init_extensions(app)
    _register_blueprints(app)
    _register_filters(app)
    register_context_processors(app)
    register_error_handlers(app)
    _register_request_hooks(app)

    from .cli import register_cli_commands

    register_cli_commands(app)
    return app


def _configure_logging(app: Flask) -> None:
    if app.debug:
        return

    app.logger.setLevel(getattr(logging, app.config["FILE_LOG_LEVEL"].upper(), logging.INFO))
    log_path = Path(app.config["LOG_DIR"]) / "application.log"
    has_rotating_handler = any(isinstance(handler, RotatingFileHandler) for handler in app.logger.handlers)
    if not has_rotating_handler:
        file_handler = RotatingFileHandler(log_path, maxBytes=2 * 1024 * 1024, backupCount=5, encoding="utf-8")
        file_handler.setLevel(app.logger.level)
        file_handler.setFormatter(logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s"))
        app.logger.addHandler(file_handler)


def _ensure_directories(app: Flask) -> None:
    required_paths = (
        Path(app.instance_path),
        Path(app.config["DATA_ROOT"]),
        Path(app.config["UPLOAD_ROOT"]),
        Path(app.config["TEMP_UPLOAD_DIR"]),
        Path(app.config["LOG_DIR"]),
    )
    for path in required_paths:
        path.mkdir(parents=True, exist_ok=True)


def _configure_runtime_environment(app: Flask) -> None:
    temp_dir = app.config["TEMP_UPLOAD_DIR"]
    os.environ["TMPDIR"] = temp_dir
    os.environ["TMP"] = temp_dir
    os.environ["TEMP"] = temp_dir


def _apply_proxy_fix(app: Flask) -> None:
    if app.config.get("TRUST_PROXY_FIX"):
        app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1)


def _init_extensions(app: Flask) -> None:
    db.init_app(app)
    migrate.init_app(app, db)
    csrf.init_app(app)
    limiter.init_app(app)
    login_manager.init_app(app)
    login_manager.login_view = "auth.login"
    login_manager.login_message_category = "warning"
    login_manager.session_protection = "strong"

    from .models import User

    @login_manager.user_loader
    def load_user(user_id: str) -> User | None:
        return User.query.get(int(user_id))


def _register_blueprints(app: Flask) -> None:
    from .admin import admin_bp
    from .api import api_bp
    from .auth import auth_bp
    from .companies import companies_bp
    from .files import files_bp
    from .main import main_bp
    from .notifications import notifications_bp
    from .reports import reports_bp
    from .tickets import tickets_bp
    from .users import users_bp

    app.register_blueprint(auth_bp)
    app.register_blueprint(main_bp)
    app.register_blueprint(tickets_bp, url_prefix="/tickets")
    app.register_blueprint(files_bp, url_prefix="/files")
    app.register_blueprint(companies_bp, url_prefix="/companies")
    app.register_blueprint(users_bp, url_prefix="/users")
    app.register_blueprint(admin_bp, url_prefix="/admin")
    app.register_blueprint(reports_bp, url_prefix="/reports")
    app.register_blueprint(notifications_bp, url_prefix="/notifications")
    app.register_blueprint(api_bp, url_prefix="/api")


def _register_filters(app: Flask) -> None:
    app.jinja_env.filters["filesize"] = format_bytes
    app.jinja_env.filters["ltext"] = LocalizationService.localize_text


def _register_request_hooks(app: Flask) -> None:
    @app.before_request
    def enforce_active_user() -> None:
        if current_user.is_authenticated and not current_user.is_active:
            logout_user()
        if current_user.is_authenticated:
            current_app.permanent_session_lifetime = timedelta(minutes=SettingsService.session_timeout_minutes())
            return SecurityService.enforce_authenticated_session()
        return None

    @app.after_request
    def add_security_headers(response):
        response.headers.setdefault("X-Frame-Options", "DENY")
        response.headers.setdefault("X-Content-Type-Options", "nosniff")
        response.headers.setdefault("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
        response.headers.setdefault("Referrer-Policy", "strict-origin-when-cross-origin")
        response.headers.setdefault("Cross-Origin-Opener-Policy", "same-origin")
        response.headers.setdefault(
            "Content-Security-Policy",
            "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; font-src 'self' data:;",
        )
        if request.is_secure or current_app.config["PREFERRED_URL_SCHEME"] == "https":
            response.headers.setdefault("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
        if current_user.is_authenticated or request.endpoint in {"auth.login", "auth.forgot_password", "auth.reset_password"}:
            response.headers.setdefault("Cache-Control", "no-store")
        return response
