// deno-lint-disable-must-await-calls
import { Cursor } from "./cursor.ts";
import { colorize } from "./style.ts";
import { TerminalBlock, TerminalLayout } from "./TerminalLayout.ts";
import { type CLICharEvent, InputManager } from "./InputManager.ts";

// export async function cliPrompt(
//   message: string,
//   block?: TerminalBlock,
// ): Promise<string> {
//   const encoder = new TextEncoder();
//   const input: string[] = [];
//   let cursorPos = 0;

//   await Deno.stdin.setRaw(true);

//   const cursorVisible = Cursor["visible"];
//   Cursor.show();

//   let range: [number, number] = [0, 1];
//   if (block) {
//     range = block.setLines([message + " "]);
//   } else {
//     Deno.stdout.writeSync(encoder.encode(message + " "));
//   }

//   const render = () => {
//     const line = message + " " + input.join("");
//     const moveTo = `\x1b[${message.length + 2 + cursorPos}G`;

//     if (block) {
//       block.setPostRenderAction(function () {
//         Deno.stdout.writeSync(
//           encoder.encode(`\x1b[${this["lastRenderRow"]};1H`),
//         );
//         Deno.stdout.writeSync(encoder.encode(moveTo));
//       });
//       range = block.setLines([line], range);
//     } else {
//       Deno.stdout.writeSync(encoder.encode("\x1b[K" + line + moveTo));
//     }
//   };

//   render();

//   const buf = new Uint8Array(64); // large enough for most pastes
//   inputLoop:
//   while (true) {
//     const n = await Deno.stdin.read(buf);
//     if (n === null) break;

//     for (let i = 0; i < n; i++) {
//       const byte = buf[i];

//       // Ctrl+C
//       if (byte === 3) {
//         block?.clear();
//         block?.["layout"]?.clearAll();
//         await Deno.stdin.setRaw(false);
//         Deno.exit(130);
//       }

//       if (byte === 13) { // Enter
//         break inputLoop;
//       }

//       // Escape sequence?
//       if (byte === 27 && i + 1 < n && buf[i + 1] === 91) {
//         const third = buf[i + 2];
//         if (third === 68 && cursorPos > 0) cursorPos--; // Left
//         else if (third === 67 && cursorPos < input.length) cursorPos++; // Right
//         else if (third === 51 && i + 3 < n && buf[i + 3] === 126) { // Delete
//           if (cursorPos < input.length) input.splice(cursorPos, 1);
//           i += 1; // consume tilde
//         }
//         i += 2; // consume ESC [ X
//         continue;
//       }

//       // Backspace
//       if (byte === 127 || byte === 8) {
//         if (cursorPos > 0) {
//           input.splice(cursorPos - 1, 1);
//           cursorPos--;
//         }
//         continue;
//       }

//       // Delete (ASCII 46)
//       if (byte === 46 && cursorPos < input.length) {
//         input.splice(cursorPos, 1);
//         continue;
//       }

//       // Printable
//       if (byte >= 32 && byte <= 126) {
//         input.splice(cursorPos, 0, String.fromCharCode(byte));
//         cursorPos++;
//       }

//       // Other cases: ignore
//     }

//     render();
//   }

//   await Deno.stdin.setRaw(false);
//   if (!cursorVisible) {
//     Cursor.hide();
//   }
//   Deno.stdout.writeSync(encoder.encode("\n"));

//   return input.join("");
// }

export async function cliPrompt(
  message: string,
  block?: TerminalBlock,
): Promise<string> {
  const encoder = new TextEncoder();
  const input: string[] = [];
  let cursorPos = 0;
  let range: [number, number] | undefined;

  Cursor.show();

  const im = InputManager.getInstance();
  im.activate();

  const render = () => {
    const line = message + " " + input.join("");
    const moveTo = `\x1b[${message.length + 2 + cursorPos}G`;

    if (block) {
      block.setPostRenderAction(() => {
        Deno.stdout.writeSync(encoder.encode(moveTo));
      });
      range = block.setLines([line], range);
    } else {
      Deno.stdout.writeSync(encoder.encode("\r\x1b[K" + line + moveTo));
    }
  };

  const exit = () => {
    im.removeEventListener("enter", onEnter);
    im.removeEventListener("backspace", onBackspace);
    im.removeEventListener("delete", onDelete);
    im.removeEventListener("arrow-left", onLeft);
    im.removeEventListener("arrow-right", onRight);
    im.removeEventListener("char", onKey);
    Cursor.hide();
  };

  let resolve: null | ((value: string) => void) = null;

  const onEnter = () => {
    exit();
    resolve?.(input.join(""));
  };

  const onBackspace = () => {
    if (cursorPos > 0) {
      input.splice(cursorPos - 1, 1);
      cursorPos--;
      render();
    }
  };

  const onDelete = () => {
    if (cursorPos < input.length) {
      input.splice(cursorPos, 1);
      render();
    }
  };

  const onLeft = () => {
    if (cursorPos > 0) {
      cursorPos--;
      render();
    }
  };

  const onRight = () => {
    if (cursorPos < input.length) {
      cursorPos++;
      render();
    }
  };

  const onKey = (e: Event) => {
    const ke = (e as CLICharEvent).detail;
    input.splice(cursorPos, 0, ke.char);
    cursorPos++;
    render();
  };

  render();

  return await new Promise<string>((res) => {
    resolve = res;
    im.addEventListener("enter", onEnter);
    im.addEventListener("backspace", onBackspace);
    im.addEventListener("delete", onDelete);
    im.addEventListener("arrow-left", onLeft);
    im.addEventListener("arrow-right", onRight);
    im.addEventListener("char", onKey);
  });
}

export async function cliConfirm(message: string, block?: TerminalBlock) {
  const im = InputManager.getInstance();
  let inpout = "";
  function isValidInput(input: string) {
    switch (input) {
      case "y":
      case "n":
        return inpout.length === 0;
      case "e":
        return inpout === "y";
      case "s":
        return inpout === "ye";
      case "o":
        return inpout === "n";
      default:
        return false;
    }
  }

  function onKey(e: CLICharEvent) {
    const ke = e.detail;
    const char = String.fromCharCode(ke.key);
    if (isValidInput(char)) {
      inpout += char;
    } else {
      e.stopImmediatePropagation();
    }
  }
  im.addEventListener("char", onKey);
  const value = await cliPrompt(message + " (y/n)", block).then((v) =>
    v.charAt(0).toLowerCase() === "y"
  );
  im.removeEventListener("char", onKey);
  return value;
}

export async function cliAlert(message: string, block?: TerminalBlock) {
  const im = InputManager.getInstance();
  const onKey = (e: CLICharEvent) => {
    e.stopImmediatePropagation();
  };
  im.addEventListener("char", onKey);
  await cliPrompt(
    message + colorize(" Press Enter to continue", "gray"),
    block,
  );
  im.removeEventListener("char", onKey);
}

export function cliLog(
  message: string | object | Array<unknown>,
  block?: TerminalBlock,
) {
  if (!block) {
    console.log(message);
  } else {
    if (typeof message === "object") message = Deno.inspect(message);
    block.setLines(message.split("\n"));
  }
}

if (import.meta.main) {
  Cursor.hide();
  const layout = new TerminalLayout();
  const title = new TerminalBlock();
  const block = new TerminalBlock();
  const footer = new TerminalBlock();
  block.setPreserveHistory(true);
  // ScrollManager.enable(block);
  title.setLines(["Hello, World!"]);
  title.setFixedHeight(1);

  footer.setLines(["Press Ctrl+C to exit"]);
  footer.setFixedHeight(1);

  layout.register("title", title);
  layout.register("block", block);
  layout.register("footer", footer);

  InputManager.addEventListener("exit", () => {
    layout.clearAll();
    // console.clear();
    Deno.exit(0);
  });

  Deno.addSignalListener("SIGINT", () => {
    layout.clearAll();
    // console.clear();
    // Deno.exit(0);
  });
  const name = await cliPrompt("Enter your name:", block);
  cliLog(`Hello, ${name}!`, block);
  const single = await cliConfirm("Are you single?", block);
  cliLog(single ? "Do you want to go out with me?" : "Okay", block);
  // ScrollManager.enable(block);
  const loopingConvo = [
    "No response?",
    "I guess that's okay",
    "Maybe I'll see you next week?",
    "Wow, really not going to say anything to me?",
    "Well, if that's how you feel",
  ];
  let convo = 0;
  setInterval(() => {
    cliLog(loopingConvo[convo % loopingConvo.length], block);
    convo++;
  }, 2000);
  // setTimeout(async () => {
  //   await cliAlert("Well, if that's that...", block);
  //   Deno.exit(0);
  // }, 10000);
}
