import { escapeHtmlShitty, HtmlEnt, JSX } from './jsx';

const { readFileSync } = require("fs");
const contents = readFileSync(0);
const lines: string[] = contents.toString().split('\n');

type MessageFrag = {
  type: 'url',
  link: string
} | {
  type: 'text',
  content: string
} | {
  type: 'error',
  content: string
};

type IrcUser = {
  mode?: '+' | '@' | ' ',
  name: string
};

type IrcMessage = {
  time: string,
  type: 'status',
  content: MessageFrag[]
} | {
  time: string,
  type: 'action',
  user: IrcUser,
  content: MessageFrag[]
} | {
  time: string,
  type: 'message',
  user: IrcUser,
  content: MessageFrag[]
} | {
  type: 'day_change'
} | {
  type: 'redacted'
};

const URL = /(((?:https?|gopher):\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/|spotify:track:)((?:\(?[^\s()<>]+\)?)*[^ \s`!\[\]{};:\'".,<>?\xab\xbb\u201c\u201d\u2018\u2019]))/i;

function parseMessage(m: string): MessageFrag[] {
  m = m.trimEnd();
  const parts: MessageFrag[] = [];
  let match: RegExpMatchArray | null;
  while ((match = m.match(URL)) != null) {
    parts.push({ type: 'text', content: m.slice(0, match.index) });
    parts.push({ type: 'url', link: match[0] });
    m = m.slice((match.index ?? 0) + match[0].length);
  }
  if (m !== "") {
    parts.push({ type: 'text', content: m });
  }
  return parts;
}

function parse(s: string): IrcMessage {
  s = s.trimEnd();
  if (s.startsWith("--- Day changed")) {
    return {
      type: "day_change"
    };
  } else if (s === "[REDACTED]") {
    return {
      type: "redacted"
    };
  }

  const time = s.slice(0, 5);
  switch (s[6]) {
    case '-':
      return {
        time,
        type: 'status',
        content: parseMessage(s.slice(10))
      };
    case '<': {
      const mode = s[7];
      if (mode !== '@' && mode !== '+' && mode !== ' ') throw `invalid mode: ${mode}`;
      const name = s.slice(8).split('>')[0];
      return {
        time,
        type: 'message',
        user: { mode, name },
        content: parseMessage(s.slice(10 + name.length))
      }
    }
    case ' ': {
      const name = s.slice(8).split(' ')[1];
      return {
        time,
        type: 'action',
        user: { name },
        content: parseMessage(s.slice(10 + name.length))
      }
    }
  }
  return {
    time: "error",
    type: 'status',
    content: [{
      type: 'error',
      content: `could not parse message: ${s}`
    }]
  };
};

const messages = lines.map(l => l.trim()).filter(l => l !== '.' && l !== "").map((v, i) => { try { return parse(v) } catch (e) { throw `${e} ${i}` } });

type MsgGroup = {
  user?: IrcUser,
  type: "status" | "message" | "action" | "day_change" | "redacted",
  messages: IrcMessage[]
}

function group(messages: IrcMessage[]): MsgGroup[] {
  const groups: MsgGroup[] = [];

  let group: IrcMessage[] = [];
  let discrim: IrcUser | undefined = undefined;
  let type: "status" | "action" | "message" | undefined = undefined;

  for (const m of messages) {
    if (m.type === "status" || m.type === "day_change" || m.type == "redacted") {
      groups.push({
        user: discrim,
        type: type ?? "message",
        messages: group
      });

      discrim = undefined;
      type = "status";
      group = [];
      groups.push({
        type: m.type,
        messages: [m]
      })
      continue;
    } else if (discrim !== undefined && (m.user.mode === undefined || m.user.mode === discrim.mode) && m.user.name === discrim.name) {
      group.push(m)
    } else {
      if (group.length !== 0)
        groups.push({
          user: discrim,
          type: type ?? "message",
          messages: group
        });
      group = [m];
      discrim = m.user;
      type = m.type;
    }
  }

  if (group.length !== 0) {
    if (type === "status") {
      groups.push({
        user: undefined,
        type,
        messages: group
      })
    } else if (discrim !== undefined) {
      groups.push({
        user: discrim,
        type: type ?? "message",
        messages: group
      });
    }
  }

  return groups;
}

const groups = group(messages);

const render = (c: MessageFrag): JSX.Element => {
  if (c.type === "text") {
    return JSX.createTextNode(escapeHtmlShitty(c.content));
  } else if (c.type === "url") {
    return <a href={c.link}>{escapeHtmlShitty(c.link)}</a>;
  } else if (c.type === "error") {
    return <span style="color: red; font-family: monospace">{escapeHtmlShitty(c.content)}</span>;
  } else {
    throw "impossible!" // thanks TypeScript
  }
}

const Message = ({ message, inclTs }: { message: IrcMessage, inclTs?: boolean }) => {
  if (message.type === "day_change") {
    return <div class="mg-sep">
      <span>Today</span>
    </div>
  };

  if (message.type === "redacted") {
    return <div class="mg-redacted">
      [REDACTED]
    </div>;
  }

  return <div class={`mg-m mg-${message.type}`}>
    {
      (inclTs === undefined || inclTs === true) ? <span class="mg-ts"> {message.time} </span> : undefined
    }
    <span class={`mg-txt mg-${message.type}`}>
      { message.content.map(c => render(c)) }
    </span>
  </div>
}

function renderMessageGroup(m: MsgGroup): HtmlEnt {
  const u = m.user;
  const m0 = m.messages[0];

  if (u !== undefined) {
    return <div class="mg-user">
      <div class="mg-pfp">
        <img src={`https://offtopia.org/pages/people/${u.name.toLocaleLowerCase()}.jpg`} alt={`${u.name}'s profile picture`} />
      </div>
      <div class="mg-contents">
        <span class="mg-ts-u">
          <span class="mg-ts-u-u"> {m.user?.name} </span>
          <span class="mg-ts-u-ts"> {(m0.type === "day_change" || m0.type === "redacted") ? "what" : m0.time} </span>
        </span>

        <Message message={m.messages[0]} inclTs={false} />

        {
          m.messages.slice(1).map(x =>
            <Message message={x} />
          )
        }
      </div>
    </div>
  } else {
    if (m.messages.length < 1) {
      return <hr style="display: none;" />
    }

    return <div class="mg-no-user">
      {m.messages.map(x => <Message message={x} />)}
    </div>;
  }
}

const rendered = groups.map(renderMessageGroup);

const elem = <html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0"></meta>
  </head>
  <body>
    {rendered}
    <svg id="squircle-container" width="0" height="0">
      <clipPath id="squircle" clipPathUnits="objectBoundingBox">
        <path fill="red" stroke="none" d="M 0,0.5 C 0,0 0,0 0.5,0 S 1,0 1,0.5 1,1 0.5,1 0,1 0,0.5" />
      </clipPath>
    </svg>
  </body>
  <style>
    {readFileSync("style.css")}
  </style>

</html>;

console.log(elem.toHtmlStr());