package transcript

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type transcript struct {
	Metadata map[string]any `json:"metadata"`
	Segments []segment      `json:"segments"`
}

type segment struct {
	ID      int         `json:"id"`
	Start   json.Number `json:"start"`
	End     json.Number `json:"end"`
	Speaker string      `json:"speaker"`
	Text    string      `json:"text"`
}

func decodeTranscript(raw []byte) (transcript, error) {
	var fields map[string]json.RawMessage
	if err := decodeJSON(raw, &fields); err != nil {
		return transcript{}, err
	}
	if fields == nil {
		return transcript{}, fmt.Errorf("top-level value must be an object")
	}

	metadataRaw, ok := fields["metadata"]
	if !ok {
		return transcript{}, fmt.Errorf("metadata is required")
	}
	var metadata map[string]any
	if err := decodeJSON(metadataRaw, &metadata); err != nil {
		return transcript{}, fmt.Errorf("metadata must be an object: %w", err)
	}
	if metadata == nil {
		return transcript{}, fmt.Errorf("metadata must be an object")
	}

	segmentsRaw, ok := fields["segments"]
	if !ok {
		return transcript{}, fmt.Errorf("segments are required")
	}
	var segmentValues []json.RawMessage
	if err := decodeJSON(segmentsRaw, &segmentValues); err != nil {
		return transcript{}, fmt.Errorf("segments must be an array: %w", err)
	}
	if segmentValues == nil {
		return transcript{}, fmt.Errorf("segments must be an array")
	}
	segments := make([]segment, 0, len(segmentValues))
	for i, rawSegment := range segmentValues {
		segment, err := decodeSegment(rawSegment, i)
		if err != nil {
			return transcript{}, err
		}
		segments = append(segments, segment)
	}

	return transcript{
		Metadata: metadata,
		Segments: segments,
	}, nil
}

func decodeSegment(raw []byte, index int) (segment, error) {
	var fields map[string]json.RawMessage
	if err := decodeJSON(raw, &fields); err != nil {
		return segment{}, fmt.Errorf("segment[%d] must be an object: %w", index, err)
	}
	if fields == nil {
		return segment{}, fmt.Errorf("segment[%d] must be an object", index)
	}

	var decoded segment
	if err := decodeOptionalSegmentID(fields, "id", &decoded.ID); err != nil {
		return segment{}, fmt.Errorf("segment[%d] id must be a positive integer string or number: %w", index, err)
	}
	if err := decodeOptionalNumber(fields, "start", &decoded.Start); err != nil {
		return segment{}, fmt.Errorf("segment[%d] start must be a number: %w", index, err)
	}
	if err := decodeOptionalNumber(fields, "end", &decoded.End); err != nil {
		return segment{}, fmt.Errorf("segment[%d] end must be a number: %w", index, err)
	}
	if err := decodeOptionalString(fields, "speaker", &decoded.Speaker); err != nil {
		return segment{}, fmt.Errorf("segment[%d] speaker must be a string: %w", index, err)
	}
	if err := decodeOptionalString(fields, "text", &decoded.Text); err != nil {
		return segment{}, fmt.Errorf("segment[%d] text must be a string: %w", index, err)
	}
	return decoded, nil
}

func decodeOptionalString(fields map[string]json.RawMessage, key string, out *string) error {
	raw, ok := fields[key]
	if !ok {
		return nil
	}
	return decodeJSON(raw, out)
}

func decodeOptionalSegmentID(fields map[string]json.RawMessage, key string, out *int) error {
	raw, ok := fields[key]
	if !ok {
		return nil
	}

	var text string
	if err := decodeJSON(raw, &text); err == nil {
		parsed, err := parsePositiveInt(text)
		if err != nil {
			return err
		}
		*out = parsed
		return nil
	}

	var number json.Number
	if err := decodeJSON(raw, &number); err == nil {
		parsed, err := parsePositiveInt(number.String())
		if err != nil {
			return err
		}
		*out = parsed
		return nil
	}

	return fmt.Errorf("must be a positive integer string or number")
}

func parsePositiveInt(value string) (int, error) {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return 0, fmt.Errorf("must not be empty")
	}
	if trimmed != value {
		return 0, fmt.Errorf("must not contain leading or trailing whitespace")
	}
	parsed, err := strconv.Atoi(value)
	if err != nil {
		return 0, fmt.Errorf("must be an integer")
	}
	if parsed <= 0 {
		return 0, fmt.Errorf("must be positive")
	}
	if strconv.Itoa(parsed) != value {
		return 0, fmt.Errorf("must be a canonical positive integer")
	}
	return parsed, nil
}

func decodeOptionalNumber(fields map[string]json.RawMessage, key string, out *json.Number) error {
	raw, ok := fields[key]
	if !ok {
		return nil
	}
	return decodeJSON(raw, out)
}

func decodeJSON(raw []byte, out any) error {
	decoder := json.NewDecoder(bytes.NewReader(raw))
	decoder.UseNumber()
	if err := decoder.Decode(out); err != nil {
		return err
	}
	if err := decoder.Decode(&struct{}{}); err != io.EOF {
		if err == nil {
			return fmt.Errorf("unexpected trailing JSON value")
		}
		return err
	}
	return nil
}
