from fastapi.testclient import TestClient

import gateway.app as gateway_module


class DummyResponse:
    def __init__(
        self,
        status_code: int,
        headers: dict[str, str],
        json_payload: dict | None = None,
        text: str = '',
        content: bytes | None = None,
    ):
        self.status_code = status_code
        self.headers = headers
        self._json_payload = json_payload or {}
        self.text = text
        self.content = content if content is not None else text.encode("utf-8")

    def json(self) -> dict:
        return self._json_payload


class DummyAsyncClient:
    last_request: dict = {}

    def __init__(self, timeout: int):
        self.timeout = timeout

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, tb):
        return None

    async def request(self, method, url, params=None, content=None, headers=None):
        DummyAsyncClient.last_request = {
            'method': method,
            'url': url,
            'params': params,
            'content': content,
            'headers': headers or {},
        }
        return DummyResponse(200, {'content-type': 'application/json'}, {'ok': True})


def test_gateway_forwards_authorization_header(monkeypatch):
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.get(
        '/proxy/auth/auth/me',
        headers={'Authorization': 'Bearer test-token', 'X-User': 'admin', 'X-Role': 'admin'},
    )

    assert response.status_code == 200
    assert DummyAsyncClient.last_request['headers']['Authorization'] == 'Bearer test-token'
    assert DummyAsyncClient.last_request['headers']['X-User'] == 'admin'


def test_gateway_forwards_telegram_secret_header(monkeypatch):
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.post(
        '/proxy/telegram/integrations/telegram/bot/webhook',
        headers={'X-Telegram-Bot-Api-Secret-Token': 'secret-token'},
        json={'update_id': 1},
    )

    assert response.status_code == 200
    assert DummyAsyncClient.last_request['headers']['X-Telegram-Bot-Api-Secret-Token'] == 'secret-token'


def test_gateway_forwards_whatsapp_secret_header(monkeypatch):
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.post(
        '/proxy/whatsapp/integrations/whatsapp/provider/webhook',
        headers={'X-WhatsApp-Webhook-Secret': 'wa-secret'},
        json={'entry': []},
    )

    assert response.status_code == 200
    assert DummyAsyncClient.last_request['headers']['X-WhatsApp-Webhook-Secret'] == 'wa-secret'
    assert DummyAsyncClient.last_request['content'] == b'{"entry":[]}'


def test_gateway_forwards_meta_signature_header_and_raw_body(monkeypatch):
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.post(
        '/proxy/whatsapp/integrations/whatsapp/provider/webhook',
        headers={
            'X-Hub-Signature-256': 'sha256=test-signature',
            'Content-Type': 'application/json',
        },
        content=b'{"entry":[{"changes":[]}]}' ,
    )

    assert response.status_code == 200
    assert DummyAsyncClient.last_request['headers']['X-Hub-Signature-256'] == 'sha256=test-signature'
    assert DummyAsyncClient.last_request['headers']['Content-Type'] == 'application/json'
    assert DummyAsyncClient.last_request['content'] == b'{"entry":[{"changes":[]}]}'


def test_gateway_preserves_html_response(monkeypatch):
    async def request(self, method, url, params=None, content=None, headers=None):  # noqa: ANN001, ANN201
        return DummyResponse(200, {'content-type': 'text/html'}, text='<html>bridge</html>')

    monkeypatch.setattr(DummyAsyncClient, 'request', request)
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.get('/proxy/auth/auth/oidc/callback')

    assert response.status_code == 200
    assert '<html>bridge</html>' in response.text


def test_gateway_preserves_plain_text_response(monkeypatch):
    async def request(self, method, url, params=None, content=None, headers=None):  # noqa: ANN001, ANN201
        return DummyResponse(200, {'content-type': 'text/plain; charset=utf-8'}, text='123456')

    monkeypatch.setattr(DummyAsyncClient, 'request', request)
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.get('/proxy/whatsapp/integrations/whatsapp/provider/webhook')

    assert response.status_code == 200
    assert response.text == '123456'
    assert response.headers['content-type'].startswith('text/plain')


def test_gateway_forwards_delete_requests(monkeypatch):
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.delete(
        '/proxy/routing/queues/que_delete_me',
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )

    assert response.status_code == 200
    assert DummyAsyncClient.last_request['method'] == 'DELETE'
    assert DummyAsyncClient.last_request['url'].endswith('/queues/que_delete_me')


def test_gateway_prefixes_sales_public_api_paths(monkeypatch):
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.get('/proxy/sales/pipelines?limit=25')

    assert response.status_code == 200
    assert DummyAsyncClient.last_request['url'].endswith('/api/v1/pipelines')
    assert DummyAsyncClient.last_request['params'] == {'limit': '25'}


def test_gateway_does_not_prefix_sales_internal_paths(monkeypatch):
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.post('/proxy/sales/internal/sales-sync/telegram', json={'chat_id': '123'})

    assert response.status_code == 200
    assert DummyAsyncClient.last_request['url'].endswith('/internal/sales-sync/telegram')


def test_gateway_preserves_binary_audio_response(monkeypatch):
    async def request(self, method, url, params=None, content=None, headers=None):  # noqa: ANN001, ANN201
        return DummyResponse(
            200,
            {
                'content-type': 'audio/wav',
                'content-disposition': 'attachment; filename="demo-call.wav"',
            },
            content=b'RIFFdemo',
        )

    monkeypatch.setattr(DummyAsyncClient, 'request', request)
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.get('/proxy/recording/recordings/rec_demo/content')

    assert response.status_code == 200
    assert response.content == b'RIFFdemo'
    assert response.headers['content-type'].startswith('audio/wav')
    assert 'demo-call.wav' in response.headers['content-disposition']


def test_gateway_returns_bad_gateway_when_upstream_unavailable(monkeypatch):
    async def request(self, method, url, params=None, content=None, headers=None):  # noqa: ANN001, ANN201
        raise gateway_module.httpx.ConnectError("All connection attempts failed")

    monkeypatch.setattr(DummyAsyncClient, 'request', request)
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    client = TestClient(gateway_module.app)

    response = client.get('/proxy/sales/pipelines')

    assert response.status_code == 502
    assert response.json() == {
        'detail': 'Upstream service unavailable',
        'service': 'sales',
    }


def test_gateway_queues_error_alert_for_proxy_unreachable(monkeypatch):
    async def request(self, method, url, params=None, content=None, headers=None):  # noqa: ANN001, ANN201
        raise gateway_module.httpx.ConnectError("All connection attempts failed")

    alerts: list[dict] = []

    def queue_alert(
        service: str,
        method: str,
        path: str,
        status: int,
        detail: str,
        **kwargs: object,
    ) -> None:  # noqa: ARG001
        alerts.append({
            'service': service,
            'method': method,
            'path': path,
            'status': status,
            'detail': detail,
            'kwargs': kwargs,
        })

    monkeypatch.setattr(DummyAsyncClient, 'request', request)
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    monkeypatch.setattr(gateway_module, '_queue_telegram_error_alert', queue_alert)
    monkeypatch.setenv('ERROR_TELEGRAM_ALERTS', '1')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_BOT_TOKEN', 'token')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_CHAT_IDS', '999')
    client = TestClient(gateway_module.app)

    response = client.get('/proxy/sales/pipelines')

    assert response.status_code == 502
    assert alerts[0]['service'] == 'api-gateway'
    assert alerts[0]['method'] == 'GET'
    assert alerts[0]['path'] == '/proxy/sales/pipelines'
    assert alerts[0]['status'] == 502
    assert 'All connection attempts failed' in alerts[0]['detail']


def test_gateway_queues_error_alert_for_channel_alias(monkeypatch):
    async def request(self, method, url, params=None, content=None, headers=None):  # noqa: ANN001, ANN201
        raise gateway_module.httpx.ConnectError("All connection attempts failed")

    alerts: list[dict] = []

    def queue_alert(
        service: str,
        method: str,
        path: str,
        status: int,
        detail: str,
        **kwargs: object,
    ) -> None:  # noqa: ARG001
        alerts.append({
            'service': service,
            'method': method,
            'path': path,
            'status': status,
            'detail': detail,
            'kwargs': kwargs,
        })

    monkeypatch.setattr(DummyAsyncClient, 'request', request)
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    monkeypatch.setattr(gateway_module, '_queue_telegram_error_alert', queue_alert)
    monkeypatch.setenv('ERROR_TELEGRAM_ALERTS', '1')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_BOT_TOKEN', 'token')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_CHANNEL_ID', '@my_alert_channel')
    client = TestClient(gateway_module.app)

    client.get('/proxy/sales/pipelines')

    assert alerts
    assert alerts[0]['status'] == 502


def test_gateway_queues_error_alert_for_internal_exception(monkeypatch):
    async def broken_forward(method: str, service: str, path: str, request: object) -> None:  # noqa: ANN001, ANN201
        raise ValueError('proxy exploded')

    alerts: list[dict] = []

    def queue_alert(
        service: str,
        method: str,
        path: str,
        status: int,
        detail: str,
        **kwargs: object,
    ) -> None:  # noqa: ARG001
        alerts.append({
            'service': service,
            'method': method,
            'path': path,
            'status': status,
            'detail': detail,
            'kwargs': kwargs,
        })

    monkeypatch.setattr(gateway_module, '_forward', broken_forward)
    monkeypatch.setattr(gateway_module, '_queue_telegram_error_alert', queue_alert)
    monkeypatch.setenv('ERROR_TELEGRAM_ALERTS', '1')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_BOT_TOKEN', 'token')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_CHAT_IDS', '999')

    client = TestClient(gateway_module.app, raise_server_exceptions=False)

    response = client.get('/proxy/sales/pipelines')

    assert response.status_code == 500
    assert alerts
    assert alerts[0]['service'] == 'api-gateway'
    assert alerts[0]['status'] == 500
    assert alerts[0]['path'] == '/proxy/sales/pipelines'


def test_gateway_queues_error_alert_with_body_and_extra_context(monkeypatch):
    async def request(self, method, url, params=None, content=None, headers=None):  # noqa: ANN001, ANN201
        raise gateway_module.httpx.ConnectError("All connection attempts failed")

    alerts: list[dict] = []

    def queue_alert(
        service: str,
        method: str,
        path: str,
        status: int,
        detail: str,
        request=None,
        extra=None,
        **kwargs: object,
    ) -> None:  # noqa: ARG001
        alerts.append({
            'service': service,
            'method': method,
            'path': path,
            'status': status,
            'detail': detail,
            'request': request,
            'extra': extra,
        })

    monkeypatch.setattr(DummyAsyncClient, 'request', request)
    monkeypatch.setattr(gateway_module.httpx, 'AsyncClient', DummyAsyncClient)
    monkeypatch.setattr(gateway_module, '_queue_telegram_error_alert', queue_alert)
    monkeypatch.setenv('ERROR_TELEGRAM_ALERTS', '1')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_BOT_TOKEN', 'token')
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_CHAT_IDS', '999')
    client = TestClient(gateway_module.app)

    response = client.post(
        '/proxy/sales/pipelines?name=demo&token=super-secret',
        headers={'Authorization': 'Bearer secret-token', 'X-Request-Id': 'req-123'},
        json={'foo': 'bar'},
    )

    assert response.status_code == 502
    assert alerts[0]['status'] == 502
    assert alerts[0]['extra']['upstream_service'] == 'sales'
    assert alerts[0]['extra']['upstream_target_path'] == 'api/v1/pipelines'
    assert alerts[0]['extra']['request_body_preview'] == '{"foo":"bar"}'
    assert alerts[0]['request'] is not None


def test_gateway_normalizes_channel_alias_for_t_me(monkeypatch):
    monkeypatch.setenv('ERROR_TELEGRAM_ALERT_CHANNEL_ID', 't.me/konturaitelecom')

    assert gateway_module._error_alert_chat_ids() == ['@konturaitelecom']
