import os
import sys
import types

import pandas as pd
import pytest

from mnemosynecore import sharepoint as sp


class _Resp:
    def __init__(self, content=b"ok", status_ok=True):
        self.content = content
        self._status_ok = status_ok

    def raise_for_status(self):
        if not self._status_ok:
            raise RuntimeError("http error")


def _install_fake_ntlm(monkeypatch):
    class FakeNtlmAuth:
        def __init__(self, username, password):
            self.username = username
            self.password = password

    module = types.ModuleType("requests_ntlm")
    module.HttpNtlmAuth = FakeNtlmAuth
    monkeypatch.setitem(sys.modules, "requests_ntlm", module)
    return FakeNtlmAuth


def test_sharepoint_download_file_with_conn_id(monkeypatch):
    FakeNtlmAuth = _install_fake_ntlm(monkeypatch)
    captured = {}

    monkeypatch.setattr(
        sp,
        "_resolve_sharepoint_config",
        lambda conn, dir_path=None: {
            "host": "https://sp.local",
            "login": "svc",
            "password": "pwd",
            "domain": "O3",
        },
    )
    monkeypatch.setattr(
        sp.requests,
        "get",
        lambda url, auth=None, headers=None, timeout=None, verify=None, allow_redirects=None: (
            captured.update(
                {
                    "url": url,
                    "auth": auth,
                    "headers": headers,
                    "timeout": timeout,
                    "verify": verify,
                    "allow_redirects": allow_redirects,
                }
            )
            or _Resp(content=b"data")
        ),
    )

    out = sp.sharepoint_download_file(
        sharepoint_conn="SP_CONN",
        file_url="/sites/analytics/report.csv",
        headers={"X-Test": "1"},
        timeout=10,
        verify=False,
    )
    assert out == b"data"
    assert captured["url"] == "https://sp.local/sites/analytics/report.csv"
    assert isinstance(captured["auth"], FakeNtlmAuth)
    assert captured["auth"].username == "O3\\svc"
    assert captured["auth"].password == "pwd"
    assert captured["headers"]["X-Test"] == "1"
    assert captured["timeout"] == 10
    assert captured["verify"] is False


def test_sharepoint_download_file_absolute_url(monkeypatch):
    _install_fake_ntlm(monkeypatch)
    captured = {}

    monkeypatch.setattr(
        sp,
        "_resolve_sharepoint_config",
        lambda conn, dir_path=None: {
            "host": "https://sp.local",
            "login": "svc",
            "password": "pwd",
            "domain": None,
        },
    )
    monkeypatch.setattr(
        sp.requests,
        "get",
        lambda url, auth=None, headers=None, timeout=None, verify=None, allow_redirects=None: (
            captured.update({"url": url, "auth": auth}) or _Resp(content=b"x")
        ),
    )

    out = sp.sharepoint_download_file(
        sharepoint_conn="SP_CONN",
        file_url="https://other.local/file.txt",
    )
    assert out == b"x"
    assert captured["url"] == "https://other.local/file.txt"
    assert captured["auth"].username == "svc"


def test_sharepoint_download_file_http_error(monkeypatch):
    _install_fake_ntlm(monkeypatch)
    monkeypatch.setattr(
        sp,
        "_resolve_sharepoint_config",
        lambda conn, dir_path=None: {
            "host": "https://sp.local",
            "login": "svc",
            "password": "pwd",
        },
    )
    monkeypatch.setattr(
        sp.requests,
        "get",
        lambda *args, **kwargs: _Resp(status_ok=False),
    )
    with pytest.raises(RuntimeError, match="http error"):
        sp.sharepoint_download_file(sharepoint_conn="SP_CONN", file_url="/f")


def test_sharepoint_download_file_auth_none(monkeypatch):
    captured = {}
    monkeypatch.setattr(
        sp,
        "_resolve_sharepoint_config",
        lambda conn, dir_path=None: {"host": "https://sp.local"},
    )
    monkeypatch.setattr(
        sp.requests,
        "get",
        lambda url, auth=None, headers=None, timeout=None, verify=None, allow_redirects=None: (
            captured.update({"auth": auth}) or _Resp(content=b"n")
        ),
    )

    out = sp.sharepoint_download_file(
        sharepoint_conn="SP_CONN",
        file_url="/f",
        auth_type="none",
    )
    assert out == b"n"
    assert captured["auth"] is None


def test_sharepoint_download_to_file(monkeypatch, tmp_path):
    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: b"abc")
    out = sp.sharepoint_download_to_file(
        sharepoint_conn="SP_CONN",
        file_url="/x.csv",
        output_path=str(tmp_path / "sub" / "x.csv"),
    )
    assert out.endswith("x.csv")
    assert (tmp_path / "sub" / "x.csv").read_bytes() == b"abc"


def test_sharepoint_read_text(monkeypatch):
    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: "привет".encode("utf-8"))
    assert sp.sharepoint_read_text(sharepoint_conn="SP", file_url="/a.txt") == "привет"


def test_sharepoint_read_dataframe_csv(monkeypatch):
    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: b"a,b\n1,2\n")
    df = sp.sharepoint_read_dataframe(sharepoint_conn="SP", file_url="/a.csv")
    assert list(df.columns) == ["a", "b"]
    assert df.iloc[0]["a"] == 1


def test_sharepoint_read_dataframe_tsv(monkeypatch):
    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: b"a\tb\n1\t2\n")
    df = sp.sharepoint_read_dataframe(sharepoint_conn="SP", file_url="/a.tsv")
    assert list(df.columns) == ["a", "b"]


def test_sharepoint_read_dataframe_json(monkeypatch):
    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: b'[{"a":1},{"a":2}]')
    df = sp.sharepoint_read_dataframe(sharepoint_conn="SP", file_url="/a.json")
    assert list(df["a"]) == [1, 2]


def test_sharepoint_read_dataframe_excel(monkeypatch):
    captured = {}

    def fake_read_excel(buffer, **kwargs):
        captured["is_bytesio"] = hasattr(buffer, "read")
        return pd.DataFrame({"a": [1], "b": [2]})

    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: b"excel-bytes")
    monkeypatch.setattr(sp.pd, "read_excel", fake_read_excel)
    df = sp.sharepoint_read_dataframe(sharepoint_conn="SP", file_url="/x.xlsx")
    assert list(df.columns) == ["a", "b"]
    assert captured["is_bytesio"] is True


def test_sharepoint_read_dataframe_unknown_auto(monkeypatch):
    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: b"x")
    with pytest.raises(ValueError, match="Не удалось определить формат"):
        sp.sharepoint_read_dataframe(sharepoint_conn="SP", file_url="/x.unknown")


def test_sharepoint_read_dataframe_unknown_explicit(monkeypatch):
    monkeypatch.setattr(sp, "sharepoint_download_file", lambda **kwargs: b"x")
    with pytest.raises(ValueError, match="file_format должен быть"):
        sp.sharepoint_read_dataframe(sharepoint_conn="SP", file_url="/x.any", file_format="parquet")


def test_sharepoint_read_csv_wrapper(monkeypatch):
    captured = {}
    monkeypatch.setattr(
        sp,
        "sharepoint_read_dataframe",
        lambda **kwargs: captured.update(kwargs) or pd.DataFrame({"a": [1]}),
    )
    df = sp.sharepoint_read_csv(sharepoint_conn="SP", file_url="/a.csv")
    assert list(df["a"]) == [1]
    assert captured["file_format"] == "csv"


def test_sharepoint_read_excel_wrapper(monkeypatch):
    captured = {}
    monkeypatch.setattr(
        sp,
        "sharepoint_read_dataframe",
        lambda **kwargs: captured.update(kwargs) or pd.DataFrame({"a": [1]}),
    )
    sp.sharepoint_read_excel(sharepoint_conn="SP", file_url="/a.xlsx")
    assert captured["file_format"] == "excel"


def test_sharepoint_read_json_wrapper(monkeypatch):
    captured = {}
    monkeypatch.setattr(
        sp,
        "sharepoint_read_dataframe",
        lambda **kwargs: captured.update(kwargs) or pd.DataFrame({"a": [1]}),
    )
    sp.sharepoint_read_json(sharepoint_conn="SP", file_url="/a.json")
    assert captured["file_format"] == "json"


def test_sharepoint_read_sql_wrapper(monkeypatch):
    monkeypatch.setattr(sp, "sharepoint_read_text", lambda **kwargs: "select 1;")
    assert sp.sharepoint_read_sql(sharepoint_conn="SP", file_url="/q.sql") == "select 1;"


def test_sharepoint_download_many(monkeypatch, tmp_path):
    calls = []
    monkeypatch.setattr(
        sp,
        "sharepoint_download_to_file",
        lambda **kwargs: calls.append(kwargs) or kwargs["output_path"],
    )
    out = sp.sharepoint_download_many(
        sharepoint_conn="SP",
        files=[
            {"file_url": "/a.csv"},
            {"url": "/b.csv", "file_name": "b2.csv"},
        ],
        output_dir=str(tmp_path),
    )
    assert len(out["downloaded"]) == 2
    assert out["failed"] == []
    assert calls[1]["output_path"].endswith("b2.csv")


def test_sharepoint_download_many_continue_on_error(monkeypatch, tmp_path):
    def fake_download(**kwargs):
        if kwargs["file_url"] == "/bad.csv":
            raise RuntimeError("bad")
        return kwargs["output_path"]

    monkeypatch.setattr(sp, "sharepoint_download_to_file", fake_download)
    out = sp.sharepoint_download_many(
        sharepoint_conn="SP",
        files=[{"file_url": "/ok.csv"}, {"file_url": "/bad.csv"}],
        output_dir=str(tmp_path),
        continue_on_error=True,
    )
    assert len(out["downloaded"]) == 1
    assert len(out["failed"]) == 1


def test_sharepoint_download_many_requires_url(tmp_path):
    with pytest.raises(ValueError):
        sp.sharepoint_download_many(
            sharepoint_conn="SP",
            files=[{"file_name": "x.csv"}],
            output_dir=str(tmp_path),
        )


def test_sharepoint_read_csv_test_supports_creds_path(monkeypatch, tmp_path):
    creds_file = tmp_path / "sp_creds.json"
    creds_file.write_text(
        '{"host":"https://sp.local","login":"svc","password":"pwd"}',
        encoding="utf-8",
    )
    captured = {}

    monkeypatch.setattr(
        sp,
        "sharepoint_read_csv",
        lambda **kwargs: captured.update(kwargs) or pd.DataFrame({"a": [1]}),
    )

    df = sp.sharepoint_read_csv_test(
        sharepoint_conn="SP_CONN",
        file_url="/a.csv",
        creds_path=str(creds_file),
    )
    assert list(df["a"]) == [1]
    assert captured["sharepoint_conn"]["host"] == "https://sp.local"
