from __future__ import annotations

import asyncio
import logging
from dataclasses import dataclass
from typing import Optional

# Underlying Go bindings module (generated by gopy)
from masktunnellib import masktunnel as gomasktunnel  # type: ignore

from ._utils import _logger


@dataclass
class ServerOptions:
    addr: str = ""
    port: str = "8080"
    user_agent: str = ""
    payload: str = ""
    upstream_proxy: str = ""
    username: str = ""
    password: str = ""
    verbose: int = 0


class Server:
    """MaskTunnel proxy server.

    Notes:
        - `start()` is blocking, so typical usage is `asyncio.to_thread(server.start)`.
        - Use `stop()` to shutdown.
    """

    def __init__(self, *, options: Optional[ServerOptions] = None, logger: Optional[logging.Logger] = None) -> None:
        if logger is None:
            logger = _logger
        self._logger = logger

        if options is None:
            opt = gomasktunnel.DefaultServerOption()
        else:
            opt = gomasktunnel.DefaultServerOption()
            opt.Addr = options.addr
            opt.Port = options.port
            opt.UserAgent = options.user_agent
            opt.Payload = options.payload
            opt.UpstreamProxy = options.upstream_proxy
            opt.Username = options.username
            opt.Password = options.password
            opt.Verbose = int(options.verbose)

        self._raw = gomasktunnel.NewServerHandle(opt)

    @property
    def addr(self) -> str:
        return self._raw.Addr()

    def start(self) -> None:
        self._raw.Start()

    async def async_start(self) -> None:
        await asyncio.to_thread(self._raw.Start)

    def stop(self) -> None:
        self._raw.Stop()

    async def async_stop(self) -> None:
        await asyncio.to_thread(self._raw.Stop)

    def reset_sessions(self) -> int:
        return int(self._raw.ResetSessions())

    def set_upstream_proxy(self, proxy_url: str) -> None:
        self._raw.SetUpstreamProxy(proxy_url)

    def get_ca_pem(self) -> bytes:
        return bytes(self._raw.GetCAPEM() or b"")

    def close(self) -> None:
        try:
            self._raw.Close()
        except Exception:
            pass

    async def async_close(self) -> None:
        await asyncio.to_thread(self.close)
