package main

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"runtime"

	"example.org/loop-invariant-teaching/baseline"
	"example.org/loop-invariant-teaching/corrected"
	"example.org/loop-invariant-teaching/weak"
)

type Case struct {
	ID            string `json:"id"`
	Input         []int  `json:"input"`
	ExpectedIndex int    `json:"expectedIndex"`
}
type Snapshot struct {
	Nil    bool  `json:"nil"`
	Values []int `json:"values"`
}

func snapshot(items []int) Snapshot {
	values := make([]int, len(items))
	copy(values, items)
	return Snapshot{Nil: items == nil, Values: values}
}
func main() {
	if len(os.Args) != 2 {
		panic("usage: observe CASES_JSON")
	}
	data, err := os.ReadFile(os.Args[1])
	if err != nil {
		panic(err)
	}
	var fixture struct {
		Cases []Case `json:"cases"`
	}
	if err := json.Unmarshal(data, &fixture); err != nil {
		panic(err)
	}
	type Variant struct {
		Name string
		Call func([]int) int
	}
	variants := []Variant{{"baseline", baseline.FirstNegative}, {"corrected", corrected.FirstNegative}, {"weak", weak.FirstNegative}}
	records := make([]any, 0)
	for _, variant := range variants {
		fn := runtime.FuncForPC(reflect.ValueOf(variant.Call).Pointer())
		file, line := fn.FileLine(fn.Entry())
		for _, item := range fixture.Cases {
			var input []int
			if item.Input != nil {
				input = make([]int, len(item.Input))
				copy(input, item.Input)
			}
			before := snapshot(input)
			actual := variant.Call(input)
			records = append(records, map[string]any{"variant": variant.Name, "case": item.ID, "argumentBefore": before, "returnedIndex": actual, "argumentAfter": snapshot(input), "callable": fn.Name(), "functionFile": file, "functionLine": line})
		}
	}
	output := map[string]any{"method": "Actual exported Go function calls; compiler coverage counts independently retain entry-block execution. Independent Python literal oracle evaluates output after calls.", "runtime": runtime.Version(), "goos": runtime.GOOS, "goarch": runtime.GOARCH, "calls": records}
	bytes, err := json.MarshalIndent(output, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(bytes))
}
