import asyncio
import locale
from typing import Any

from . import bbyte, bpath
from .btype import XPath


async def winscp(winscp_exe: XPath, key_file: str, server: str, cmd_list: list[str], show_cmd: bool = True):
    log_file = bpath.user('executeWinScp.log')
    bpath.remove(log_file)
    ary = [
        'option batch abort',
        'option transfer binary',
        f'open sftp://{server} -privatekey={key_file} -hostkey=*',
    ]
    ary += cmd_list
    ary += [
        'close',
        'exit',
    ]
    # /console
    cmd = f'{winscp_exe} /log={log_file} /loglevel=0 /command ' + ' '.join('%s' % x for x in ary)
    if show_cmd:
        print(cmd)
    return await run(cmd)


async def run_expect(*args: Any, output: str = '', error: str = ''):
    output_bytes, error_bytes, _ = await run_quiet(*args)
    if output and output not in bbyte.decode(output_bytes):
        raise Exception(f'命令执行失败: {" ".join([str(x) for x in args])}')
    if error and error not in bbyte.decode(error_bytes):
        raise Exception(f'命令执行失败: {" ".join([str(x) for x in args])}')


async def run_quiet(*args: Any):
    proc = await asyncio.create_subprocess_shell(
        ' '.join([str(x) for x in args]),
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )
    return await proc.communicate() + (proc.returncode or 0,)


async def run(*args: Any):
    proc = await asyncio.create_subprocess_shell(
        ' '.join([str(x) for x in args]),
    )
    await proc.communicate()
    return proc.returncode or 0


async def run2(*cmd: Any, isPrint: bool = True, encoding: str = '') -> tuple[str, int]:
    encoding = encoding or locale.getpreferredencoding()
    process = await asyncio.create_subprocess_shell(
        ' '.join([str(x) for x in cmd]),
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )
    assert process.stdout and process.stderr, '初始化进程失败'

    resultList: list[str] = []
    taskList = [
        asyncio.create_task(_handleStream(process.stdout, isPrint, encoding, resultList)),
        asyncio.create_task(_handleStream(process.stderr, isPrint, encoding, resultList)),
    ]

    await process.wait()
    for task in taskList:
        task.cancel()

    return '\n'.join(resultList), process.returncode or 0


async def _handleStream(stream: asyncio.StreamReader, isPrint: bool, encoding: str, resultList: list[str]) -> None:
    while True:
        line = await stream.readline()
        if line:
            msg = line.decode(encoding).replace('\r\n', '\n')
            if msg.endswith('\n'):
                msg = msg[:-1]
            resultList.append(msg)
            if isPrint:
                print(msg)
        else:
            break
