import logging
import mimetypes
import os
import json
from typing import Dict, List, Optional

from mattermostdriver import Driver
from mnemosynecore.vault.univ_conn import un_conn


def get_mattermost_driver(bot_id: str) -> Driver:
    if bot_id.startswith('{'):
        try:
            cfg = json.loads(bot_id)
        except json.JSONDecodeError:
            raise ValueError("Неверный формат JSON для конфигурации Mattermost")

        driver = Driver({
            "url": cfg["host"],
            "token": cfg["password"],
            "scheme": cfg.get("schema", "https"),
            "port": int(cfg.get("port", 443)),
            "basepath": cfg.get("basepath", "/api/v4"),
        })
        driver.login()
        return driver

    return un_conn(bot_id, "mattermost")


def _resolve_test_bot_id(
    bot_id: str,
    dir_path: Optional[str] = None,
    creds_path: Optional[str] = None,
) -> str:
    from mnemosynecore.vault.client import get_secret_test

    raw = bot_id.strip()
    if raw.startswith("{"):
        try:
            json.loads(raw)
        except json.JSONDecodeError as exc:
            raise ValueError("Неверный формат JSON для конфигурации Mattermost") from exc
        return raw

    if creds_path is None:
        cfg = get_secret_test(bot_id, dir_path=dir_path)
    else:
        cfg = get_secret_test(
            bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        )
    return json.dumps(cfg)


def get_mattermost_driver_test(
    bot_id: str,
    dir_path: Optional[str] = None,
    creds_path: Optional[str] = None,
) -> Driver:
    return get_mattermost_driver(
        _resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        )
    )


def send_message(
    *,
    channel_id: str,
    bot_id: str,
    text: str,
    root_id: Optional[str] = None,
    silent: bool = False,
) -> None:
    post_message(
        channel_id=channel_id,
        bot_id=bot_id,
        text=text,
        root_id=root_id,
        silent=silent,
    )


def post_message(
    *,
    channel_id: str,
    bot_id: str,
    text: str,
    root_id: Optional[str] = None,
    props: Optional[Dict[str, object]] = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    driver = get_mattermost_driver(bot_id)
    options = {"channel_id": channel_id, "message": text.strip()}
    if root_id:
        options["root_id"] = root_id
    if props:
        options["props"] = props

    try:
        post = driver.posts.create_post(options=options)
        post_id = post.get("id") if isinstance(post, dict) else None
        if not silent:
            if root_id:
                logging.info(
                    "Сообщение отправлено в тред Mattermost: channel_id=%s, root_id=%s",
                    channel_id,
                    root_id,
                )
            else:
                logging.info("Сообщение отправлено в Mattermost: %s", channel_id)
        return {
            "channel_id": channel_id,
            "post_id": post_id,
            "root_id": root_id,
        }
    except Exception as exc:
        logging.exception(
            "Ошибка отправки сообщения в Mattermost (channel_id=%s)", channel_id
        )
        raise


def send_thread_message(
    *,
    channel_id: str,
    bot_id: str,
    root_id: str,
    text: str,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    if not root_id:
        raise ValueError("root_id обязателен для отправки сообщения в тред")
    return post_message(
        channel_id=channel_id,
        bot_id=bot_id,
        text=text,
        root_id=root_id,
        silent=silent,
    )


def send_thread_messages(
    *,
    channel_id: str,
    bot_id: str,
    root_id: str,
    messages: List[str],
    silent: bool = False,
    continue_on_error: bool = False,
) -> Dict[str, List[Dict[str, Optional[str]]]]:
    if not root_id:
        raise ValueError("root_id обязателен для отправки сообщений в тред")
    sent: List[Dict[str, Optional[str]]] = []
    failed: List[Dict[str, Optional[str]]] = []
    for message in messages:
        try:
            result = send_thread_message(
                channel_id=channel_id,
                bot_id=bot_id,
                root_id=root_id,
                text=message,
                silent=silent,
            )
            sent.append(result)
        except Exception as exc:
            if continue_on_error:
                failed.append(
                    {
                        "channel_id": channel_id,
                        "root_id": root_id,
                        "post_id": None,
                        "error": str(exc),
                    }
                )
                continue
            raise
    return {"sent": sent, "failed": failed}


def send_file_bytes(
    *,
    channel_id: str,
    bot_id: str,
    file_name: str,
    file_bytes: bytes,
    text: str = "",
    mime_type: Optional[str] = None,
    root_id: Optional[str] = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    if not file_name:
        raise ValueError("file_name обязателен")
    if not isinstance(file_bytes, (bytes, bytearray)):
        raise TypeError("file_bytes должен быть bytes или bytearray")

    payload_bytes = bytes(file_bytes)
    mm_mime_type = mime_type or "application/octet-stream"

    driver = get_mattermost_driver(bot_id)
    upload = driver.files.upload_file(
        channel_id=channel_id,
        files={"files": (file_name, payload_bytes, mm_mime_type)},
    )

    file_infos = upload.get("file_infos") if isinstance(upload, dict) else None
    if not file_infos:
        raise ValueError("Mattermost не вернул file_infos после загрузки файла")

    first_file = file_infos[0]
    file_id = first_file.get("id") if isinstance(first_file, dict) else str(first_file)
    message = text.strip() if text else " "

    post = driver.posts.create_post(
        options={
            "channel_id": channel_id,
            "message": message,
            "file_ids": [file_id],
            **({"root_id": root_id} if root_id else {}),
        }
    )
    post_id = post.get("id") if isinstance(post, dict) else None

    if not silent:
        logging.info(
            "Файл отправлен в Mattermost: channel_id=%s, file_name=%s",
            channel_id,
            file_name,
        )
    return {
        "channel_id": channel_id,
        "file_name": file_name,
        "file_id": file_id,
        "post_id": post_id,
        "root_id": root_id,
    }


def send_file(
    *,
    channel_id: str,
    bot_id: str,
    file_path: str,
    text: str = "",
    file_name: Optional[str] = None,
    mime_type: Optional[str] = None,
    root_id: Optional[str] = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    if not os.path.isfile(file_path):
        raise FileNotFoundError(file_path)

    final_file_name = file_name or os.path.basename(file_path)
    final_mime_type = mime_type or mimetypes.guess_type(final_file_name)[0] or "application/octet-stream"

    with open(file_path, "rb") as f:
        payload = f.read()

    return send_file_bytes(
        channel_id=channel_id,
        bot_id=bot_id,
        file_name=final_file_name,
        file_bytes=payload,
        text=text,
        mime_type=final_mime_type,
        root_id=root_id,
        silent=silent,
    )


def send_files(
    *,
    channel_id: str,
    bot_id: str,
    file_paths: List[str],
    text: str = "",
    root_id: Optional[str] = None,
    silent: bool = False,
) -> List[Dict[str, Optional[str]]]:
    if not file_paths:
        return []
    results = []
    for file_path in file_paths:
        result = send_file(
            channel_id=channel_id,
            bot_id=bot_id,
            file_path=file_path,
            text=text,
            root_id=root_id,
            silent=silent,
        )
        results.append(result)
    return results


def send_dataframe_as_csv(
    *,
    channel_id: str,
    bot_id: str,
    df,
    file_name: str = "dataframe.csv",
    text: str = "",
    include_index: bool = False,
    encoding: str = "utf-8",
    root_id: Optional[str] = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    import pandas as pd

    if not isinstance(df, pd.DataFrame):
        raise TypeError("df должен быть pandas.DataFrame")

    csv_bytes = df.to_csv(index=include_index).encode(encoding)
    return send_file_bytes(
        channel_id=channel_id,
        bot_id=bot_id,
        file_name=file_name,
        file_bytes=csv_bytes,
        text=text,
        mime_type="text/csv",
        root_id=root_id,
        silent=silent,
    )


def send_dataframe_preview(
    *,
    channel_id: str,
    bot_id: str,
    df,
    title: str = "",
    max_rows: int = 10,
    max_cols: int = 10,
    root_id: Optional[str] = None,
    silent: bool = False,
) -> None:
    import pandas as pd

    if not isinstance(df, pd.DataFrame):
        raise TypeError("df должен быть pandas.DataFrame")
    if max_rows < 1 or max_cols < 1:
        raise ValueError("max_rows и max_cols должны быть >= 1")

    preview = df.head(max_rows).iloc[:, :max_cols]
    preview_text = preview.to_string(index=False)
    header = f"{title.strip()}\n" if title else ""
    text = (
        f"{header}"
        f"rows={len(df)}, cols={len(df.columns)}, preview_rows={len(preview)}, preview_cols={len(preview.columns)}\n"
        f"```\n{preview_text}\n```"
    )
    send_message(
        channel_id=channel_id,
        bot_id=bot_id,
        text=text,
        root_id=root_id,
        silent=silent,
    )


def send_message_test(
    *,
    channel_id: str,
    bot_id: str,
    text: str,
    dir_path: str | None = None,
    creds_path: str | None = None,
    root_id: Optional[str] = None,
    silent: bool = False,
) -> None:
    send_message(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        text=text,
        root_id=root_id,
        silent=silent,
    )


def post_message_test(
    *,
    channel_id: str,
    bot_id: str,
    text: str,
    dir_path: str | None = None,
    creds_path: str | None = None,
    root_id: Optional[str] = None,
    props: Optional[Dict[str, object]] = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    return post_message(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        text=text,
        root_id=root_id,
        props=props,
        silent=silent,
    )


def send_file_bytes_test(
    *,
    channel_id: str,
    bot_id: str,
    file_name: str,
    file_bytes: bytes,
    text: str = "",
    mime_type: Optional[str] = None,
    root_id: Optional[str] = None,
    dir_path: str | None = None,
    creds_path: str | None = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    return send_file_bytes(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        file_name=file_name,
        file_bytes=file_bytes,
        text=text,
        mime_type=mime_type,
        root_id=root_id,
        silent=silent,
    )


def send_file_test(
    *,
    channel_id: str,
    bot_id: str,
    file_path: str,
    text: str = "",
    file_name: Optional[str] = None,
    mime_type: Optional[str] = None,
    root_id: Optional[str] = None,
    dir_path: str | None = None,
    creds_path: str | None = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    return send_file(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        file_path=file_path,
        text=text,
        file_name=file_name,
        mime_type=mime_type,
        root_id=root_id,
        silent=silent,
    )


def send_files_test(
    *,
    channel_id: str,
    bot_id: str,
    file_paths: List[str],
    text: str = "",
    root_id: Optional[str] = None,
    dir_path: str | None = None,
    creds_path: str | None = None,
    silent: bool = False,
) -> List[Dict[str, Optional[str]]]:
    return send_files(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        file_paths=file_paths,
        text=text,
        root_id=root_id,
        silent=silent,
    )


def send_dataframe_as_csv_test(
    *,
    channel_id: str,
    bot_id: str,
    df,
    file_name: str = "dataframe.csv",
    text: str = "",
    include_index: bool = False,
    encoding: str = "utf-8",
    root_id: Optional[str] = None,
    dir_path: str | None = None,
    creds_path: str | None = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    return send_dataframe_as_csv(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        df=df,
        file_name=file_name,
        text=text,
        include_index=include_index,
        encoding=encoding,
        root_id=root_id,
        silent=silent,
    )


def send_dataframe_preview_test(
    *,
    channel_id: str,
    bot_id: str,
    df,
    title: str = "",
    max_rows: int = 10,
    max_cols: int = 10,
    root_id: Optional[str] = None,
    dir_path: str | None = None,
    creds_path: str | None = None,
    silent: bool = False,
) -> None:
    send_dataframe_preview(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        df=df,
        title=title,
        max_rows=max_rows,
        max_cols=max_cols,
        root_id=root_id,
        silent=silent,
    )


def send_thread_message_test(
    *,
    channel_id: str,
    bot_id: str,
    root_id: str,
    text: str,
    dir_path: str | None = None,
    creds_path: str | None = None,
    silent: bool = False,
) -> Dict[str, Optional[str]]:
    return send_thread_message(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        root_id=root_id,
        text=text,
        silent=silent,
    )


def send_thread_messages_test(
    *,
    channel_id: str,
    bot_id: str,
    root_id: str,
    messages: List[str],
    dir_path: str | None = None,
    creds_path: str | None = None,
    silent: bool = False,
    continue_on_error: bool = False,
) -> Dict[str, List[Dict[str, Optional[str]]]]:
    return send_thread_messages(
        channel_id=channel_id,
        bot_id=_resolve_test_bot_id(
            bot_id=bot_id,
            dir_path=dir_path,
            creds_path=creds_path,
        ),
        root_id=root_id,
        messages=messages,
        silent=silent,
        continue_on_error=continue_on_error,
    )
