import React, { useCallback, useState, useEffect } from "react";

/*
  ____          _              ____  _        _            
 |  _ \ ___  __| |_   ___  __ / ___|| |_ __ _| |_ ___  ___ 
 | |_) / _ \/ _` | | | \ \/ / \___ \| __/ _` | __/ _ \/ __|
 |  _ <  __/ (_| | |_| |>  <   ___) | || (_| | ||  __/\__ \
 |_| \_\___|\__,_|\__,_/_/\_\ |____/ \__\__,_|\__\___||___/
                                                           
*/
import { useSelector, useDispatch } from "react-redux";
import { updateCurrentTerminalCommand } from "../../states/global";
import {
  AppState,
  updateSetToUploadFile,
  updateSetToReloadPath,
  updateIsDeviceUploadingFile,
  updatePrintData
} from "../../states/global";

/*
     _          _   ____  
    / \   _ __ | |_|  _ \ 
   / _ \ | '_ \| __| | | |
  / ___ \| | | | |_| |_| |
 /_/   \_\_| |_|\__|____/ 
                           
*/
import { Button } from "antd";
import { notification } from "antd";
import { upload } from "../../utils/fileManager";
import openNotificationWithIcon from "../../utils/notifications";
import FileManagerUpload from "../fileManager/Upload"
import {
  AbortController,
  Consumable,
  WritableStream,
} from "@yume-chan/stream-extra";

/*
  ____               ____                                          _ ____  _          _ _ 
 |  _ \ _   _ _ __  / ___|___  _ __ ___  _ __ ___   __ _ _ __   __| / ___|| |__   ___| | |
 | |_) | | | | '_ \| |   / _ \| '_ ` _ \| '_ ` _ \ / _` | '_ \ / _` \___ \| '_ \ / _ \ | |
 |  _ <| |_| | | | | |__| (_) | | | | | | | | | | | (_| | | | | (_| |___) | | | |  __/ | |
 |_| \_\\__,_|_| |_|\____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|\__,_|____/|_| |_|\___|_|_|
                                                                                          
*/
const RunCommandShell: React.FC = () => {
  const [api, contextHolder] = notification.useNotification();
  const [command, setCommand] = useState<any>("");
  const [runState, setRunState] = useState(false);

  const currentDevice = useSelector((state: AppState) => state.currentDevice);
  const pythonCode = useSelector((state: AppState) => state.blocklyCode);
  const printData = useSelector((state: AppState) => state.printData);
  const isBackendConnected = useSelector(
    (state: AppState) => state.isBackendConnected,
  );
  const isDeviceUploadingFile = useSelector(
    (state: AppState) => state.isDeviceUploadingFile,
  );
  const dispatch = useDispatch();
  useEffect(() => {
    
  }, [currentDevice]);
  const handleOnSearch = async () => {
    console.log("开始上传文件", pythonCode);
    try {
      // await uploadFile()
      // await uploadFileOut();
      await runCode();
    } catch (e: any) {
      console.log("上传错误：", e);
      openNotificationWithIcon(
        api,
        "error",
        "bottomRight",
        "Failed to upload file",
        e,
      );
    } finally {
      console.log("上传完成");
      // openNotificationWithIcon(
      //   api,
      //   "success",
      //   "bottomRight",
      //   "Successfully uploaded file",
      //   "Uploaded to /mnt/UDISK/user_latest_code.py",
      // );
    }
  }
  const uploadFile = async () => {
    const deviceSync = currentDevice?.sync();
    if (!deviceSync) {
      return;
    }
    const strText = `import traceback
import logging
import sys
import os
import time

logger = logging.getLogger()
logger.setLevel(logging.INFO)

file_handler = logging.FileHandler('/root/output.log')
file_handler.setLevel(logging.INFO)

formatter = logging.Formatter('%(message)s')
file_handler.setFormatter(formatter)

logger.addHandler(file_handler)

def log_output(*args, **kwargs):
    output = ' '.join(map(str, args))
    logger.info(output.strip(), **kwargs)

sys.stderr.write = sys.stdout.write = log_output

os.close(1)
os.close(2)

try:
    source_code = r'''${pythonCode}
'''
    exec(source_code)
except Exception as e:
    error_info = traceback.format_exc()
    print(error_info)
`;
    const file = new File([strText], "event", {
      type: "text/plain",
    });
    try {
      dispatch(updateIsDeviceUploadingFile(true));
      dispatch(updateSetToUploadFile(true));
      await upload(file, "/root/", await deviceSync);
    } catch (e: any) {
      openNotificationWithIcon(
        api,
        "error",
        "bottomRight",
        "Failed to upload file",
        e,
      );
    } finally {
      dispatch(updateSetToUploadFile(false));
      dispatch(updateSetToReloadPath(true));
      dispatch(updateIsDeviceUploadingFile(false));
    }
  }
  const uploadFileOut = async () => {
    const deviceSync = currentDevice?.sync();
    if (!deviceSync) {
      return;
    }
    let out = `import asyncio
import subprocess
import linecache
async def tailf(filename):
    command = "python -u /root/event"
    process = subprocess.Popen(command, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    print("")
    while True:
        await asyncio.sleep(0.03)
        try:
            linecache.clearcache()
            logarr = linecache.getlines(filename)[-20:]
            if len(logarr) == 0:
                if process.poll() is None:
                    continue
                else:
                    return
            open(filename, 'w').close()
            print("".join(logarr).strip())
        except asyncio.TimeoutError:
            pass

filename = "/root/output.log"
loop = asyncio.get_event_loop()
loop.run_until_complete(tailf(filename))
loop.close()
`
    const fileOut = new File([out], "out.py", {
      type: "text/plain",
    });
    try {
      dispatch(updateIsDeviceUploadingFile(true));
      dispatch(updateSetToUploadFile(true));
      await upload(fileOut, "/root/", await deviceSync)
    } catch (e: any) {
      openNotificationWithIcon(
        api,
        "error",
        "bottomRight",
        "Failed to upload file",
        e,
      );
    } finally {
      dispatch(updateSetToUploadFile(false));
      dispatch(updateSetToReloadPath(true));
      dispatch(updateIsDeviceUploadingFile(false));
    }

  }
  const runCode = async () => {
    if (command) {
      command.kill().then(() => {
        window.device?.subprocess.shell(`(echo '' > /root/output.log ; touch /tmp/disable) && python -u /root/out.py;`).then(e => {
          setCommand(e);
          serverPrint(e);
        }).catch(err => {
          console.log(err);
        })
      })
    } else {
      window.device?.subprocess.shell(`(echo '' > /root/output.log ; touch /tmp/disable) && python -u /root/out.py;`).then(e => {
        setCommand(e);
        serverPrint(e);
      }).catch(err => {
        console.log(err);
      })
    }
  }

  const serverPrint = async (e: any) => {
    let _socketAbortController = new AbortController()
    setRunState(true);
    let data = printData;
    e._stdout.pipeTo(new WritableStream<Uint8Array>({
      write: (chunk) => {
        // Decode the chunk to obtain the output text
        const outputText = new TextDecoder().decode(chunk);

        if (outputText.length > 2) {
          data = `<p>${outputText}</p>` + data;
          dispatch(updatePrintData(data));
        }

        // this.output.push(outputText);

      },
    }),
      {
        signal: _socketAbortController.signal,
      },
    ).catch((error: any) => {
      console.error("Error writing to writable stream:", error);
    }
    );
  }
  const stopOperation = () => {
    // dispatch(updateCurrentTerminalCommand(`rm /tmp/disable`));
    command.kill().then(() => {
      window.device?.subprocess.shell(`rm /tmp/disable`).then(e => {
        setCommand(e);
        setRunState(false);
      })
    })
  }
  return (
    <>
      {contextHolder}
      <Button type="primary" disabled={!isBackendConnected || isDeviceUploadingFile} onClick={() => handleOnSearch()}>运行</Button>
      <FileManagerUpload />
      <Button type="primary" disabled={!runState} onClick={() => stopOperation()}>停止</Button>
    </>
  );
};

export default RunCommandShell;
