import os
import ssl
import socket
import datetime


def _get_local_ips():
    """Возвращает список локальных IP-адресов машины."""
    ips = []
    try:
        for info in socket.getaddrinfo(socket.gethostname(), None, socket.AF_INET):
            ip = info[4][0]
            if ip not in ips:
                ips.append(ip)
    except Exception:
        pass
    return ips


def get_ssl_context():
    """
    Создаёт собственный мини-CA и подписывает серверный сертификат.
    CA-сертификат можно установить на телефон один раз — и предупреждения исчезнут.
    Файлы: ca.pem (корневой, для установки на клиенты), cert.pem + key.pem (сервер).
    """
    cert_dir = os.path.dirname(os.path.abspath(__file__))
    ca_cert_file = os.path.join(cert_dir, "ca.pem")
    ca_key_file = os.path.join(cert_dir, "ca_key.pem")
    cert_file = os.path.join(cert_dir, "cert.pem")
    key_file = os.path.join(cert_dir, "key.pem")

    need_regen = not all(os.path.exists(f) for f in [ca_cert_file, ca_key_file, cert_file, key_file])

    if need_regen:
        try:
            from cryptography import x509
            from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
            from cryptography.hazmat.primitives import hashes, serialization
            from cryptography.hazmat.primitives.asymmetric import rsa
            import ipaddress
        except ImportError:
            print("[SSL] Библиотека cryptography не установлена.")
            print("[SSL] Установите:  pip install cryptography")
            print("[SSL] Без HTTPS GPS телефона работать не будет.")
            return None

        print("[SSL] Генерация корневого CA и серверного сертификата...")

        # === 1. Корневой CA ===
        ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
        ca_name = x509.Name([
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, "AIS Map CA"),
            x509.NameAttribute(NameOID.COMMON_NAME, "AIS Map Root CA"),
        ])
        ca_cert = (
            x509.CertificateBuilder()
            .subject_name(ca_name)
            .issuer_name(ca_name)
            .public_key(ca_key.public_key())
            .serial_number(x509.random_serial_number())
            .not_valid_before(datetime.datetime.utcnow())
            .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650))
            .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
            .add_extension(
                x509.KeyUsage(
                    digital_signature=True, key_cert_sign=True, crl_sign=True,
                    content_commitment=False, key_encipherment=False,
                    data_encipherment=False, key_agreement=False,
                    encipher_only=False, decipher_only=False,
                ),
                critical=True,
            )
            .sign(ca_key, hashes.SHA256())
        )

        with open(ca_cert_file, "wb") as f:
            f.write(ca_cert.public_bytes(serialization.Encoding.PEM))
        with open(ca_key_file, "wb") as f:
            f.write(ca_key.private_bytes(
                serialization.Encoding.PEM,
                serialization.PrivateFormat.TraditionalOpenSSL,
                serialization.NoEncryption(),
            ))
        print(f"[SSL] CA сертификат: {ca_cert_file}")

        # === 2. Серверный сертификат, подписанный CA ===
        srv_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
        srv_name = x509.Name([
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, "AIS Map"),
            x509.NameAttribute(NameOID.COMMON_NAME, "aismap.local"),
        ])

        san_entries = [x509.DNSName("aismap.local"), x509.DNSName("localhost")]
        for iface_ip in _get_local_ips():
            try:
                san_entries.append(x509.IPAddress(ipaddress.IPv4Address(iface_ip)))
            except Exception:
                pass
        san_entries.append(x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")))

        srv_cert = (
            x509.CertificateBuilder()
            .subject_name(srv_name)
            .issuer_name(ca_name)
            .public_key(srv_key.public_key())
            .serial_number(x509.random_serial_number())
            .not_valid_before(datetime.datetime.utcnow())
            .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650))
            .add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
            .add_extension(x509.SubjectAlternativeName(san_entries), critical=False)
            .add_extension(
                x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]),
                critical=False,
            )
            .sign(ca_key, hashes.SHA256())
        )

        with open(cert_file, "wb") as f:
            f.write(srv_cert.public_bytes(serialization.Encoding.PEM))
        with open(key_file, "wb") as f:
            f.write(srv_key.private_bytes(
                serialization.Encoding.PEM,
                serialization.PrivateFormat.TraditionalOpenSSL,
                serialization.NoEncryption(),
            ))
        print(f"[SSL] Серверный сертификат: {cert_file}")

    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain(cert_file, key_file)
    return ctx


def run_http_redirect(https_port):
    """Простой HTTP-сервер на порту 80, который редиректит на HTTPS."""
    from flask import Flask, redirect, request as flask_request
    redir_app = Flask("redirect")

    @redir_app.route("/", defaults={"path": ""})
    @redir_app.route("/<path:path>")
    def redir(path):
        host = flask_request.host.split(":")[0]
        port_suffix = f":{https_port}" if https_port != 443 else ""
        return redirect(f"https://{host}{port_suffix}/{path}", code=301)

    try:
        redir_app.run(host="0.0.0.0", port=80, threaded=True)
    except Exception as e:
        print(f"[HTTP] Не удалось запустить редирект на порту 80: {e}")
