package laravel

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	neturl "net/url"
	"time"

	"webby-builder/internal/models"
)

// Client for Laravel template API
type Client struct {
	baseURL    string
	serverKey  string
	httpClient *http.Client
}

// setHeaders sets common headers on outgoing requests
func (c *Client) setHeaders(req *http.Request) {
	req.Header.Set("X-Server-Key", c.serverKey)
	req.Header.Set("User-Agent", models.HTTPUserAgent)
}

// NewClient creates a new Laravel API client
func NewClient(baseURL, serverKey string) *Client {
	return NewClientWithTimeout(baseURL, serverKey, 30*time.Second)
}

// NewClientWithTimeout creates a new Laravel API client with custom timeout
func NewClientWithTimeout(baseURL, serverKey string, timeout time.Duration) *Client {
	return &Client{
		baseURL:   baseURL,
		serverKey: serverKey,
		httpClient: &http.Client{
			Timeout: timeout,
		},
	}
}

// FetchTemplates lists available templates for the given output target
// (e.g. "website" | "wordpress_theme"). In practice the executor always passes
// a concrete target key ("website" by default); an empty string omits the
// query param, which Laravel also treats as "website" (back-compat).
func (c *Client) FetchTemplates(outputTarget string) (*models.TemplateListResponse, error) {
	url := fmt.Sprintf("%s/api/templates", c.baseURL)
	if outputTarget != "" {
		url = fmt.Sprintf("%s?output_target=%s", url, neturl.QueryEscape(outputTarget))
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	c.setHeaders(req)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	var result models.TemplateListResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}
	return &result, nil
}

// GetTemplateMetadata gets detailed template info. outputTarget scopes id/name
// resolution to the build's target (empty = website, for back-compat).
func (c *Client) GetTemplateMetadata(id, outputTarget string) (*models.TemplateMetadata, error) {
	url := fmt.Sprintf("%s/api/templates/%s", c.baseURL, id)
	if outputTarget != "" {
		url = fmt.Sprintf("%s?output_target=%s", url, neturl.QueryEscape(outputTarget))
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	c.setHeaders(req)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	var result models.TemplateMetadata
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}
	return &result, nil
}

// DownloadTemplate downloads template zip. outputTarget scopes id/name
// resolution to the build's target (empty = website, for back-compat) so a
// WordPress build's useTemplate("default") can't pull the React template.
func (c *Client) DownloadTemplate(id, outputTarget string) (io.ReadCloser, error) {
	url := fmt.Sprintf("%s/api/templates/%s/download", c.baseURL, id)
	if outputTarget != "" {
		url = fmt.Sprintf("%s?output_target=%s", url, neturl.QueryEscape(outputTarget))
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	c.setHeaders(req)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		_ = resp.Body.Close()
		return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	return resp.Body, nil
}

// DownloadContext downloads template zip with context support
func (c *Client) DownloadContext(ctx context.Context, id string) (io.ReadCloser, error) {
	url := fmt.Sprintf("%s/api/templates/%s/download", c.baseURL, id)
	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	c.setHeaders(req)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		_ = resp.Body.Close()
		return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	return resp.Body, nil
}

// FetchImageLibrary fetches all stock images from Laravel API
func (c *Client) FetchImageLibrary() (*models.ImageLibraryResponse, error) {
	url := fmt.Sprintf("%s/api/image-library", c.baseURL)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	c.setHeaders(req)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
	}

	var result models.ImageLibraryResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}
	return &result, nil
}
