package shared

import (
	"fmt"
	"os"
	"strings"
)

type Error struct {
	message string
	loc     Location
}

func NewError(loc Location, message string, a ...any) Error {
	return Error{
		message: fmt.Sprintf(message, a...),
		loc:     loc,
	}
}

func (err Error) Error() string {
	f, e := os.ReadFile(err.loc.FilePath)
	if e != nil {
		return err.message
	}

	lines := strings.Split(string(f), "\n")
	lineIdx := err.loc.LC.Line - 1
	if lineIdx < 0 || lineIdx >= len(lines) {
		return err.message
	}

	start := max(0, lineIdx-2)
	end := min(len(lines)-1, lineIdx+2)

	var b strings.Builder
	fmt.Fprintf(&b, "%s:%d:%d\n", err.loc.FilePath, err.loc.LC.Line, err.loc.LC.Col)

	const red = "\x1b[31m"
	const reset = "\x1b[0m"

	for i := start; i <= end; i++ {
		ln := i + 1
		if i == lineIdx {
			fmt.Fprintf(&b, "%s%4d |%s %s  %s(col %d)%s\n",
				red, ln, reset, lines[i], red, err.loc.LC.Col, reset)
		} else {
			fmt.Fprintf(&b, "%4d | %s\n", ln, lines[i])
		}
	}

	fmt.Fprintf(&b, "\n%s", err.message)
	return b.String()
}

type LineCol struct {
	Line int
	Col  int
}

type Location struct {
	LC       LineCol
	FilePath string
}

func (l Location) String() string {
	return fmt.Sprintf("%s:%d:%d", l.FilePath, l.LC.Line, l.LC.Col)
}

type Pair[T1 any, T2 any] struct {
	L T1
	R T2
}