package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Configuration
var (
	baseURL      = getEnv("API_URL", "http://localhost:8080/api/v1")
	testEmail    = getEnv("TEST_EMAIL", "test@veza.local")
	testPassword = getEnv("TEST_PASSWORD", "TestPassword123!")
)

func main() {
	fmt.Println("🚀 Starting Veza Smoke Tests...")
	fmt.Printf("Target: %s\n", baseURL)

	// 1. Authentication
	fmt.Println("\n🔐 Authenticating...")

token, err := authenticate()
	if err != nil {
		fatal("Authentication failed: %v", err)
	}
	fmt.Println("✅ Authentication successful")

	// 2. Create Dummy Audio File
	fmt.Println("\n🎵 Creating dummy audio file...")
	filePath, err := createDummyAudioFile()
	if err != nil {
		fatal("Failed to create dummy file: %v", err)
	}
	defer os.Remove(filePath)
	fmt.Printf("✅ Created dummy audio file: %s\n", filePath)

	// 3. Upload Track
	fmt.Println("\n⬆️ Uploading track...")

trackID, err := uploadTrack(token, filePath)
	if err != nil {
		fatal("Track upload failed: %v", err)
	}
	fmt.Printf("✅ Track uploaded successfully (ID: %v)\n", trackID)

	// 4. Verify Webhook / Status (Polling)
	fmt.Println("\n🔄 Waiting for processing...")
	err = waitForProcessing(token, trackID)
	if err != nil {
		fmt.Printf("⚠️ Processing warning: %v (might be expected if workers are not running)\n", err)
	} else {
		fmt.Println("✅ Track processed")
	}

	// 5. Playback Verification (Metadata check)
	fmt.Println("\n▶️ Verifying playback accessibility...")
	err = verifyPlayback(token, trackID)
	if err != nil {
		fatal("Playback verification failed: %v", err)
	}
	fmt.Println("✅ Playback metadata accessible")

	fmt.Println("\n🎉 All smoke tests passed!")
}

func getEnv(key, fallback string) string {
	if value, ok := os.LookupEnv(key);
	ok {
		return value
	}
	return fallback
}

func fatal(format string, args ...interface{}) {
	fmt.Printf("❌ "+format+"\n", args...)
	os.Exit(1)
}

type LoginResponse struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
}

func authenticate() (string, error) {
	jsonData := map[string]interface{}{
		"email":       testEmail,
		"password":    testPassword,
		"remember_me": false,
	}
	jsonBytes, _ := json.Marshal(jsonData)

	req, err := http.NewRequest("POST", baseURL+"/auth/login", bytes.NewBuffer(jsonBytes))
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("login failed (status %d): %s", resp.StatusCode, string(body))
	}

	var result LoginResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return "", err
	}

	if result.AccessToken == "" {
		return "", fmt.Errorf("no access token received")
	}

	return result.AccessToken, nil
}

func createDummyAudioFile() (string, error) {
	// Create a 1-second silent wav file or just random bytes pretending to be mp3
	// For smoke test validation, random bytes might be rejected if validation is strict (magic numbers).
	// Let's create a file with MP3 magic number if possible, or just use text and hope validation is loose enough for this test,
	// OR actually generate a valid minimal mp3 header.
	// MP3 magic number: ID3 or FF FB.
	
	// Minimal fake MP3
	filename := "smoke_test_track.mp3"
	f, err := os.Create(filename)
	if err != nil {
		return "", err
	}
	defer f.Close()

	// ID3 header + junk
	f.Write([]byte("ID3\x03\x00\x00\x00\x00\x00\x0a")) // Fake ID3 tag
	f.Write(make([]byte, 1024*10)) // 10KB of junk

	return filename, nil
}

type UploadResponse struct {
	Message string `json:"message"`
	Track   struct {
		ID string `json:"id"`
	} `json:"track"`
}

func uploadTrack(token, filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("file", filepath.Base(filePath))
	if err != nil {
		return 0, err
	}
	io.Copy(part, file)
	writer.Close()

	req, err := http.NewRequest("POST", baseURL+"/tracks/upload", body)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())
	req.Header.Set("Authorization", "Bearer "+token)

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != http.StatusCreated {
		return "", fmt.Errorf("upload failed (status %d): %s", resp.StatusCode, string(respBody))
	}

	var result UploadResponse
	if err := json.Unmarshal(respBody, &result); err != nil {
		return "", fmt.Errorf("failed to parse upload response: %v. Body: %s", err, string(respBody))
	}

	if result.Track.ID == "" {
		return "", fmt.Errorf("no track ID returned")
	}

	return result.Track.ID, nil
}

type TrackStatusResponse struct {
	Status string `json:"status"`
}

func waitForProcessing(token string, trackID string) error {
	client := &http.Client{Timeout: 5 * time.Second}
	maxRetries := 5 // Short wait
	
	for i := 0; i < maxRetries; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("%s/tracks/%s", baseURL, trackID), nil)
		if err != nil {
			return err
		}
		req.Header.Set("Authorization", "Bearer "+token)

		resp, err := client.Do(req)
		if err != nil {
			fmt.Printf("  Attempt %d: Error %v\n", i+1, err)
			time.Sleep(1 * time.Second)
			continue
		}
		defer resp.Body.Close()
		
		if resp.StatusCode == 200 {
			// Parsing status
			// Assuming GET /tracks/:id returns the track object with a 'status' field
			var track map[string]interface{}
			json.NewDecoder(resp.Body).Decode(&track)
			status, _ := track["status"].(string)
			fmt.Printf("  Attempt %d: Status = %s\n", i+1, status)
			
			if status == "ready" || status == "processing" || status == "uploading" {
				// processing/uploading is acceptable for "Upload successful" smoke test
				// ready means transcoding finished
				return nil
			}
		} else {
			fmt.Printf("  Attempt %d: Status Code %d\n", i+1, resp.StatusCode)
		}
		
		time.Sleep(1 * time.Second)
	}
	
	return fmt.Errorf("track did not reach stable state")
}

func verifyPlayback(token string, trackID string) error {
	// Check if we can get the track details. 
	// The actual stream URL might be in the track details or a separate endpoint.
	// Based on API Spec: GET /tracks/:id
	
	req, err := http.NewRequest("GET", fmt.Sprintf("%s/tracks/%s", baseURL, trackID), nil)
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "Bearer "+token)

	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return fmt.Errorf("failed to get track details (status %d)", resp.StatusCode)
	}
	
	return nil
}
