package media

import (
	"bufio"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func ParseVideoInput(input string) (string, string) {
	// YouTube URL patterns
	youtubePatterns := []*regexp.Regexp{
		regexp.MustCompile(`(?:youtube\.com/watch\?v=|youtu\.be/)([a-zA-Z0-9_-]{11})`),
	}
	
	// Instagram URL patterns
	instagramPatterns := []*regexp.Regexp{
		regexp.MustCompile(`instagram\.com/(?:p|reels)/([a-zA-Z0-9_-]+)`),
	}
	
	// Check YouTube patterns
	for _, pattern := range youtubePatterns {
		if match := pattern.FindStringSubmatch(input); match != nil {
			return match[1], "youtube"
		}
	}
	
	// Check Instagram patterns
	for _, pattern := range instagramPatterns {
		if match := pattern.FindStringSubmatch(input); match != nil {
			return match[1], "instagram"
		}
	}
	
	// Check if it's a raw YouTube ID (11 characters, no dots)
	if IsYouTubeID(input) {
		return input, "youtube"
	}
	
	// Check if it's a raw Instagram ID (assume anything else that's alphanumeric)
	if IsInstagramID(input) {
		return input, "instagram"
	}
	
	return "", "unknown"
}

func DownloadVideo(videoID, platform string) (string, error) {
	switch platform {
	case "youtube":
		return DownloadYouTube(videoID)
	case "instagram":
		return DownloadInstagram(videoID)
	default:
		return "", fmt.Errorf("unsupported platform: %s", platform)
	}
}

func GetCookieFile(platform string) string {
	switch platform {
	case "youtube":
		return "./cookies_youtube.txt"
	case "instagram":
		return "./cookies_instagram.txt"
	default:
		return fmt.Sprintf("./cookies_%s.txt", platform)
	}
}

func EnsureWav(videoID string) error {
	wavPath := fmt.Sprintf("./data/%s.wav", videoID)
	if _, err := os.Stat(wavPath); err == nil {
		fmt.Printf("WAV %s already exists, skipping creation\n", wavPath)
		return nil
	}

	cmd := exec.Command(
		"ffmpeg",
		"-y",
		"-i", fmt.Sprintf("./data/%s.mp4", videoID),
		"-vn",
		"-acodec", "pcm_s16le",
		"-ar", "44100",
		"-ac", "2",
		wavPath,
	)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("ffmpeg failed creating WAV: %v", err)
	}

	return nil
}

func extractSAPISID(cookieFile string) (string, error) {
	file, err := os.Open(cookieFile)
	if err != nil {
		return "", fmt.Errorf("failed to open cookie file: %v", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "#") || line == "" {
			continue
		}
		
		parts := strings.Split(line, "\t")
		if len(parts) >= 7 {
			cookieName := parts[5]
			cookieValue := parts[6]
			
			if cookieName == "SAPISID" || cookieName == "__Secure-3PAPISID" {
				return cookieValue, nil
			}
		}
	}
	
	if err := scanner.Err(); err != nil {
		return "", fmt.Errorf("error reading cookie file: %v", err)
	}
	
	return "", fmt.Errorf("SAPISID not found in cookies")
}

func generatePOToken(sapisid string) string {
	timestamp := time.Now().Unix()
	timestampStr := strconv.FormatInt(timestamp, 10)
	
	hashInput := timestampStr + " " + sapisid + " https://www.youtube.com"
	hash := sha1.Sum([]byte(hashInput))
	hashHex := fmt.Sprintf("%x", hash)
	
	return timestampStr + "_" + hashHex
}

func getStoredPOTokenFromServer() string {
	resp, err := http.Get("http://localhost:3009/po-token")
	if err != nil {
		fmt.Printf("Debug: Failed to get PO token from server: %v\n", err)
		return ""
	}
	defer resp.Body.Close()
	
	if resp.StatusCode != http.StatusOK {
		return ""
	}
	
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("Debug: Failed to read PO token response: %v\n", err)
		return ""
	}
	
	var response struct {
		POToken string `json:"poToken"`
	}
	
	if err := json.Unmarshal(body, &response); err != nil {
		fmt.Printf("Debug: Failed to parse PO token response: %v\n", err)
		return ""
	}
	
	return response.POToken
}

func getPOToken(cookieFile string) string {
	// First try to get PO token from extension via server
	if storedToken := getStoredPOTokenFromServer(); storedToken != "" {
		fmt.Printf("Debug: Using PO token from extension: %s...\n", storedToken[:20])
		// Format the stored token for yt-dlp (assuming it's already base64url encoded)
		formattedToken := fmt.Sprintf("web.gvs+%s,web.subs+%s", storedToken, storedToken)
		fmt.Printf("Debug: Formatted stored PO token: %s...\n", formattedToken[:50])
		return formattedToken
	}
	
	fmt.Printf("Debug: No stored PO token available, trying SAPISID method...\n")
	
	sapisid, err := extractSAPISID(cookieFile)
	if err != nil {
		fmt.Printf("Debug: Failed to extract SAPISID: %v\n", err)
		return ""
	}
	fmt.Printf("Debug: Extracted SAPISID: %s\n", sapisid)
	
	poToken := generatePOToken(sapisid)
	fmt.Printf("Debug: Generated raw PO token: %s\n", poToken)
	
	// Format as CLIENT.CONTEXT+TOKEN for yt-dlp
	// Use web.gvs for video downloads and web.subs for subtitles
	formattedToken := fmt.Sprintf("web.gvs+%s,web.subs+%s", poToken, poToken)
	fmt.Printf("Debug: Formatted generated PO token: %s\n", formattedToken)
	
	return formattedToken
}