// Native and MediaConvert MP4 examples. Submission requires -submit.
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/mediaconvert"
	"github.com/aws/aws-sdk-go-v2/service/mediaconvert/types"
)

func main() {
	mode := flag.String("mode", "native", "native or mediaconvert")
	endpoint := flag.String("endpoint", "https://silken.io", "Public API endpoint")
	input := flag.String("input-url", "", "Presigned input GET URL")
	output := flag.String("output-url", "", "Presigned output PUT URL")
	verify := flag.String("verify-url", "", "Presigned output GET/HEAD URL (native only)")
	role := flag.String("role", "presigned-input", "Registered input role ARN for S3 input")
	submit := flag.Bool("submit", false, "Create a billable job")
	flag.Parse()
	if *input == "" || *output == "" {
		fail("input-url and output-url are required")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	key := os.Getenv("SILKEN_API_KEY")
	switch *mode {
	case "native":
		if *verify == "" {
			fail("verify-url is required for native verified delivery")
		}
		if !*submit {
			fmt.Println("Ready: native MP4 job, sdr_720p. Add -submit to create it.")
			return
		}
		var result struct{ ID, Status string }
		err := post(ctx, *endpoint, key, "/v1/jobs", map[string]any{"profile": "sdr_720p", "input_url": *input, "output_url": *output, "verify_url": *verify}, &result)
		if err != nil {
			fail(err.Error())
		}
		fmt.Println(result.ID, result.Status)
	case "mediaconvert":
		if key == "" {
			fail("SILKEN_API_KEY is required")
		}
		client := mediaconvert.NewFromConfig(aws.Config{Region: "us-east-1", Credentials: credentials.NewStaticCredentialsProvider(key, key, ""), RetryMaxAttempts: 1}, func(o *mediaconvert.Options) { o.BaseEndpoint = aws.String(*endpoint) })
		_, err := client.DescribeEndpoints(ctx, &mediaconvert.DescribeEndpointsInput{})
		if err != nil {
			fail("MediaConvert endpoint discovery failed")
		}
		// SDK Go structs do not marshal to AWS REST field names with encoding/json.
		// Build the validator boundary explicitly; use typed SDK input to submit.
		job := map[string]any{"role": *role, "settings": map[string]any{
			"inputs": []any{map[string]any{"fileInput": *input}},
			"outputGroups": []any{map[string]any{
				"outputGroupSettings": map[string]any{"type": "FILE_GROUP_SETTINGS", "fileGroupSettings": map[string]any{"destination": *output}},
				"outputs":             []any{map[string]any{"containerSettings": map[string]any{"container": "MP4"}, "videoDescription": map[string]any{"height": 720, "codecSettings": map[string]any{"codec": "H_264"}}, "audioDescriptions": []any{map[string]any{"codecSettings": map[string]any{"codec": "AAC"}}}}},
			}},
		}}
		var verdict struct{ Valid bool }
		if err = post(ctx, *endpoint, key, "/v1/mediaconvert/validate", job, &verdict); err != nil {
			fail(err.Error())
		}
		if !verdict.Valid {
			fail("Job validation failed; inspect fields with the docs validator")
		}
		fmt.Println("MediaConvert translation passed; endpoint discovery succeeded.")
		if !*submit {
			return
		}
		result, err := client.CreateJob(ctx, &mediaconvert.CreateJobInput{Role: role, Settings: &types.JobSettings{
			Inputs:       []types.Input{{FileInput: input}},
			OutputGroups: []types.OutputGroup{{OutputGroupSettings: &types.OutputGroupSettings{Type: types.OutputGroupTypeFileGroupSettings, FileGroupSettings: &types.FileGroupSettings{Destination: output}}, Outputs: []types.Output{{ContainerSettings: &types.ContainerSettings{Container: types.ContainerTypeMp4}, VideoDescription: &types.VideoDescription{Height: aws.Int32(720), CodecSettings: &types.VideoCodecSettings{Codec: types.VideoCodecH264}}, AudioDescriptions: []types.AudioDescription{{CodecSettings: &types.AudioCodecSettings{Codec: types.AudioCodecAac}}}}}}},
		}})
		if err != nil {
			fail("MediaConvert submission failed")
		}
		fmt.Println(aws.ToString(result.Job.Id), result.Job.Status)
	default:
		fail("mode must be native or mediaconvert")
	}
}

func post(ctx context.Context, endpoint, key, path string, body, result any) error {
	if key == "" {
		return fmt.Errorf("SILKEN_API_KEY is required")
	}
	data, err := json.Marshal(body)
	if err != nil {
		return err
	}
	req, err := http.NewRequestWithContext(ctx, "POST", strings.TrimRight(endpoint, "/")+path, bytes.NewReader(data))
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "Bearer "+key)
	req.Header.Set("Content-Type", "application/json")
	response, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("request failed")
	}
	defer response.Body.Close()
	if response.StatusCode < 200 || response.StatusCode >= 300 {
		return fmt.Errorf("API returned HTTP %d", response.StatusCode)
	}
	return json.NewDecoder(io.LimitReader(response.Body, 1<<20)).Decode(result)
}

func fail(message string) { fmt.Fprintln(os.Stderr, message); os.Exit(1) }
