import visit from 'unist-util-visit';

const TIMESTAMP_NODE_TYPE = 'timestamp';

// ***************************************************************************
// Tokenize timestamp
// ***************************************************************************

function findNextTimestamp(value, fromIndex, strictlyFromIndex) {
  let begin = 0;
  while (begin < value.length) {
    // Start with a rough match
    const match = value.substring(begin).match(/[0-9:]+/);

    if (!match) {
      return null;
    }

    // Compensate 'substring' index. 'match.index' is relative to 'value' from now on.
    match.index += begin;

    if (strictlyFromIndex && match.index !== fromIndex) {
      if (match.index > fromIndex) {
        // Already gone past desired index. Skip the rest.
        return null;
      } else {
        // Next match might fit 'fromIndex'.
        begin = match.index + match[0].length;
        continue;
      }
    }

    if (fromIndex > 0 && fromIndex >= match.index && fromIndex < match.index + match[0].length) {
      // Skip previously-rejected word, preventing "62:01" from being tokenized as "2:01", for example.
      // This assumes that a non-zero 'fromIndex' means that a previous lookup has failed.
      begin = match.index + match[0].length;
      continue;
    }

    // Exclude trailing colons to allow "0:12: Start of section", for example.
    const str = match[0].replace(/:+$/, '');

    let isValidTimestamp;
    switch (str.length) {
      case 4: // "9:59"
        isValidTimestamp = /^[0-9]:[0-5][0-9]$/.test(str);
        break;
      case 5: // "59:59"
        isValidTimestamp = /^[0-5][0-9]:[0-5][0-9]$/.test(str);
        break;
      case 7: // "9:59:59"
        isValidTimestamp = /^[0-9]:[0-5][0-9]:[0-5][0-9]$/.test(str);
        break;
      case 8: // "99:59:59"
        isValidTimestamp = /^[0-9][0-9]:[0-5][0-9]:[0-5][0-9]$/.test(str);
        break;
      default:
        // Reject
        isValidTimestamp = false;
        break;
    }

    if (isValidTimestamp) {
      // Profit!
      return {
        text: str,
        index: match.index,
      };
    }

    if (strictlyFromIndex && match.index >= fromIndex) {
      return null; // Since it failed and we've gone past the desired index, skip the rest.
    }

    begin = match.index + match[0].length;
  }

  return null;
}

function locateTimestamp(value, fromIndex) {
  const ts = findNextTimestamp(value, fromIndex, false);
  return ts ? ts.index : -1;
}

// Generate 'timestamp' markdown node
const createTimestampNode = (text) => ({
  type: TIMESTAMP_NODE_TYPE,
  value: text,
  children: [{ type: 'text', value: text }],
});

// Generate a markdown link from timestamp
function tokenizeTimestamp(eat, value, silent) {
  if (silent) {
    return true;
  }

  const ts = findNextTimestamp(value, 0, true);
  if (ts) {
    try {
      const text = ts.text;
      return eat(text)(createTimestampNode(text));
    } catch (err) {
      // Do nothing
    }
  }
}

tokenizeTimestamp.locator = locateTimestamp;
tokenizeTimestamp.notInList = true; // Flag doesn't work? It'll always tokenizes in List and never in Bullet.
tokenizeTimestamp.notInLink = true;
tokenizeTimestamp.notInBlock = true;

export function inlineTimestamp() {
  const Parser = this.Parser;
  const tokenizers = Parser.prototype.inlineTokenizers;
  const methods = Parser.prototype.inlineMethods;

  // Add an inline tokenizer (defined in the following example).
  tokenizers.timestamp = tokenizeTimestamp;

  // Run it just before `text`.
  methods.splice(methods.indexOf('text'), 0, 'timestamp');
}

// ***************************************************************************
// Format timestamp
// ***************************************************************************

function strToSeconds(stime) {
  const tt = stime.split(':').reverse();
  return (tt.length >= 3 ? +tt[2] : 0) * 60 * 60 + (tt.length >= 2 ? +tt[1] : 0) * 60 + (tt.length >= 1 ? +tt[0] : 0);
}

const transformer = (node, index, parent) => {
  if (node.type === TIMESTAMP_NODE_TYPE && parent && parent.type === 'paragraph') {
    const timestampStr = node.value;
    const seconds = strToSeconds(timestampStr);

    node.type = 'link';
    node.url = `?t=${seconds}`;
    node.title = timestampStr;
    node.children = [{ type: 'text', value: timestampStr }];
  }
};

const transform = (tree) => {
  visit(tree, [TIMESTAMP_NODE_TYPE], transformer);
};

export const formattedTimestamp = () => transform;