import { ParseError } from "src/lang/parser/parser";
import { renderSpan, SourceText } from "src/lang/parser/source_text";
import { DisplayLineViews } from "./LineView";

export function formatErrorMesage(err: ParseError): string {
  switch (err.tag) {
    case "UnexpectedToken":
      return `Unexpected token. Expected: ${err.expected}`;
    
    case "UnexpectedTokenWhileParsingSequence":
      return `Unexpected token in sequence. Expected delimiter ${formatChar(err.expectedDelimiter)} or terminator ${formatChar(err.expectedTerminator)}, but found ${formatChar(err.received)}.`;

    case "UnexpectedCharacter":
      return `Unexpected character: ${formatChar(err.char)}`;

    case "UnexpectedEOF":
      return "Unexpected end of file.";

    case "ExpectedNumber":
      return "Expected a number here.";

    case "InvalidNumber":
      return err.reason === "NotFinite" 
        ? "Number is too large or invalid." 
        : "Invalid number format (missing fractional digits?).";

    case "InvalidIdentifier":
       // Handle nested reasons if needed, e.g. "Keyword 'let' cannot be used as an identifier"
      return `Invalid identifier '${err.text}': ${err.reason.tag}`;

    case "InvalidEscape":
      switch (err.reason.tag) {
        case "UnknownEscapeSequence": return `Unknown escape sequence: \\${formatChar(err.reason.char)}`;
        case "UnicodeMissingBrace": return "Unicode escape missing opening brace '{'.";
        case "UnicodeNoDigits": return "Unicode escape missing hex digits.";
        case "UnicodeUnclosed": return "Unicode escape missing closing brace '}'.";
        case "UnicodeOverflow": return `Unicode code point ${err.reason.value.toString(16)} is out of bounds.`;
      }

    // Context specific errors
    case "ExpectedExpression": return "Expected an expression here.";
    case "ExpectedFieldAssignmentSymbol": return "Expected '=' for field assignment.";
    case "ExpectedPatternAssignmentSymbol": return "Expected '=' for pattern assignment.";
    case "ExpectedPatternBindingSymbol": return "Expected '.' for pattern binding.";
    case "ExpectedFunctionCallStart": return "Expected '(' to start function call.";
    case "ExpectedRecordOpen": return "Expected '(' to start record.";
    case "ExpectedLetBlockOpen": return "Expected '{' to start let-block.";
    case "ExpectedLetBlockClose": return "Expected '}' to close let-block.";
    case "ExpectedMatchBlockOpen": return "Expected '{' to start match-block.";
    case "ExpectedMatchBlockClose": return "Expected '}' to close match-block.";
    case "ExpectedLambdaBlockOpen": return "Expected '{' to start lambda body.";
    case "ExpectedLambdaBlockClose": return "Expected '}' to close lambda body.";
    case "ExpectedApplyStart": return "Expected '(' after 'apply'.";
    case "ExpectedApplySeparator": return "Expected '!' inside 'apply'.";
    case "UnexpectedTagPattern": return "Unexpected tag pattern (expected product pattern).";
    case "ExpectedPattern": return "Expected a pattern here.";
    case "ExpectedRecordPatternOpen": return "Expected '(' for record pattern.";
    case "ExpectedRecordField": return "Expected a field name in record pattern.";
    
    default:
      return `Unknown error: ${(err as any).tag}`;
  }
}

// Helper to safely print code points (handling special chars like \n)
function formatChar(cp: number | undefined): string {
  // Handle EOF (undefined) or invalid numbers safely
  if (cp === undefined || Number.isNaN(cp)) {
    return "EOF";
  }

  const s = String.fromCodePoint(cp);
  
  if (s === '\n') return "\\n";
  if (s === '\r') return "\\r";
  if (s === '\t') return "\\t";
  
  return `'${s}'`;
}

export function ShowParseError(props: { text: SourceText, err: ParseError }) {
  const msg = () => formatErrorMesage(props.err);
  const views = () => renderSpan(props.text, props.err.span, 3);


  // Parse Error: Expected '(' to start function call.
  // 1 | +(23, x)
  //       ^

  return (
    <div>
      <div>
        <span style={{ color: "#ff5555", "font-weight": "bold" }}>Parse Error: </span>
        <span style={{ "font-weight": "bold" }}>{msg()}</span>
      </div>

      <DisplayLineViews views={views()} />
    </div>
  );
}

