package tools

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"strings"
	"time"

	"github.com/nextlevelbuilder/goclaw/internal/providers"
)

// callMinimaxVideoGen calls the MiniMax video generation API (async with task polling).
// Flow: POST /video_generation → poll /query/video_generation → download from file retrieve.
func callMinimaxVideoGen(ctx context.Context, apiKey, apiBase, model string, params map[string]any) ([]byte, *providers.Usage, error) {
	prompt := GetParamString(params, "prompt", "")
	duration := GetParamInt(params, "duration", 6)
	resolution := GetParamString(params, "resolution", "720P")
	promptOptimizer := GetParamBool(params, "prompt_optimizer", true)
	fastPretreatment := GetParamBool(params, "fast_pretreatment", false)

	base := strings.TrimRight(apiBase, "/")

	// 1. Submit video generation task.
	submitBody := map[string]any{
		"model":            model,
		"prompt":           prompt,
		"duration":         duration,
		"resolution":       resolution,
		"prompt_optimizer": promptOptimizer,
	}
	if fastPretreatment {
		submitBody["fast_pretreatment"] = true
	}

	jsonBody, err := json.Marshal(submitBody)
	if err != nil {
		return nil, nil, fmt.Errorf("marshal request: %w", err)
	}

	submitURL := base + "/video_generation"
	req, err := http.NewRequestWithContext(ctx, "POST", submitURL, bytes.NewReader(jsonBody))
	if err != nil {
		return nil, nil, fmt.Errorf("create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+apiKey)

	client := &http.Client{} // timeout governed by chain context
	resp, err := client.Do(req)
	if err != nil {
		return nil, nil, fmt.Errorf("http request: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, fmt.Errorf("read response: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, nil, fmt.Errorf("API error %d: %s", resp.StatusCode, truncateBytes(respBody, 500))
	}

	var submitResp struct {
		TaskID   string `json:"task_id"`
		BaseResp *struct {
			StatusCode int    `json:"status_code"`
			StatusMsg  string `json:"status_msg"`
		} `json:"base_resp"`
	}
	if err := json.Unmarshal(respBody, &submitResp); err != nil {
		return nil, nil, fmt.Errorf("parse submit response: %w", err)
	}
	if submitResp.BaseResp != nil && submitResp.BaseResp.StatusCode != 0 {
		return nil, nil, fmt.Errorf("MiniMax API error %d: %s",
			submitResp.BaseResp.StatusCode, submitResp.BaseResp.StatusMsg)
	}
	if submitResp.TaskID == "" {
		return nil, nil, fmt.Errorf("no task_id in MiniMax response: %s", truncateBytes(respBody, 300))
	}

	slog.Info("create_video: MiniMax task submitted", "task_id", submitResp.TaskID)

	// 2. Poll until done (max ~6 minutes, poll every 10s).
	pollURL := base + "/query/video_generation?task_id=" + submitResp.TaskID
	const maxPolls = 40
	const pollInterval = 10 * time.Second

	var fileID string
	for i := range maxPolls {
		select {
		case <-ctx.Done():
			return nil, nil, ctx.Err()
		case <-time.After(pollInterval):
		}

		pollReq, err := http.NewRequestWithContext(ctx, "GET", pollURL, nil)
		if err != nil {
			return nil, nil, fmt.Errorf("create poll request: %w", err)
		}
		pollReq.Header.Set("Authorization", "Bearer "+apiKey)

		pollResp, err := client.Do(pollReq)
		if err != nil {
			slog.Warn("create_video: MiniMax poll error, retrying", "error", err, "attempt", i+1)
			continue
		}

		pollBody, _ := io.ReadAll(pollResp.Body)
		pollResp.Body.Close()

		if pollResp.StatusCode != http.StatusOK {
			return nil, nil, fmt.Errorf("poll API error %d: %s", pollResp.StatusCode, truncateBytes(pollBody, 500))
		}

		var pollResult struct {
			Status   string `json:"status"`
			FileID   string `json:"file_id"`
			BaseResp *struct {
				StatusCode int    `json:"status_code"`
				StatusMsg  string `json:"status_msg"`
			} `json:"base_resp"`
		}
		if err := json.Unmarshal(pollBody, &pollResult); err != nil {
			return nil, nil, fmt.Errorf("parse poll response: %w", err)
		}

		if pollResult.BaseResp != nil && pollResult.BaseResp.StatusCode != 0 {
			return nil, nil, fmt.Errorf("MiniMax poll error %d: %s",
				pollResult.BaseResp.StatusCode, pollResult.BaseResp.StatusMsg)
		}

		slog.Info("create_video: MiniMax polling", "attempt", i+1, "status", pollResult.Status)

		switch pollResult.Status {
		case "Success":
			fileID = pollResult.FileID
		case "Failed":
			return nil, nil, fmt.Errorf("MiniMax video generation failed")
		}

		if fileID != "" {
			break
		}
	}

	if fileID == "" {
		return nil, nil, fmt.Errorf("MiniMax video generation timed out after %d polls", maxPolls)
	}

	// 3. Retrieve download URL.
	retrieveURL := base + "/files/retrieve?file_id=" + fileID
	retrieveReq, err := http.NewRequestWithContext(ctx, "GET", retrieveURL, nil)
	if err != nil {
		return nil, nil, fmt.Errorf("create retrieve request: %w", err)
	}
	retrieveReq.Header.Set("Authorization", "Bearer "+apiKey)

	retrieveResp, err := client.Do(retrieveReq)
	if err != nil {
		return nil, nil, fmt.Errorf("retrieve file: %w", err)
	}
	defer retrieveResp.Body.Close()

	retrieveBody, err := io.ReadAll(retrieveResp.Body)
	if err != nil {
		return nil, nil, fmt.Errorf("read retrieve response: %w", err)
	}
	if retrieveResp.StatusCode != http.StatusOK {
		return nil, nil, fmt.Errorf("retrieve API error %d: %s", retrieveResp.StatusCode, truncateBytes(retrieveBody, 500))
	}

	var fileResp struct {
		File *struct {
			DownloadURL string `json:"download_url"`
		} `json:"file"`
	}
	if err := json.Unmarshal(retrieveBody, &fileResp); err != nil {
		return nil, nil, fmt.Errorf("parse retrieve response: %w", err)
	}
	if fileResp.File == nil || fileResp.File.DownloadURL == "" {
		return nil, nil, fmt.Errorf("no download_url in MiniMax file response: %s", truncateBytes(retrieveBody, 300))
	}

	downloadURL := fileResp.File.DownloadURL
	slog.Info("create_video: MiniMax downloading video", "url", downloadURL)

	// 4. Download the video.
	dlReq, err := http.NewRequestWithContext(ctx, "GET", downloadURL, nil)
	if err != nil {
		return nil, nil, fmt.Errorf("create download request: %w", err)
	}

	dlClient := &http.Client{} // timeout governed by chain context
	dlResp, err := dlClient.Do(dlReq)
	if err != nil {
		return nil, nil, fmt.Errorf("download video: %w", err)
	}
	defer dlResp.Body.Close()

	if dlResp.StatusCode != http.StatusOK {
		dlBody, _ := io.ReadAll(dlResp.Body)
		return nil, nil, fmt.Errorf("download error %d: %s", dlResp.StatusCode, truncateBytes(dlBody, 300))
	}

	videoBytes, err := limitedReadAll(dlResp.Body, maxMediaDownloadBytes)
	if err != nil {
		return nil, nil, fmt.Errorf("read video data: %w", err)
	}

	return videoBytes, nil, nil
}
