package cpanel

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"time"
)

// Client handles cPanel UAPI interactions
type Client struct {
	host     string
	username string
	apiToken string
	http     *http.Client
}

// NewClient creates a new cPanel API client
func NewClient() *Client {
	return &Client{
		host:     os.Getenv("CPANEL_HOST"),
		username: os.Getenv("CPANEL_USERNAME"),
		apiToken: os.Getenv("CPANEL_API_TOKEN"),
		http: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// NewClientWithConfig creates a client with custom config
func NewClientWithConfig(host, username, apiToken string) *Client {
	return &Client{
		host:     host,
		username: username,
		apiToken: apiToken,
		http: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// EmailAccount represents a cPanel email account
type EmailAccount struct {
	Email          string `json:"email"`
	Login          string `json:"login"`
	Domain         string `json:"domain"`
	DiskUsed       string `json:"diskused"`
	DiskQuota      string `json:"diskquota"`
	HumandiskUsed  string `json:"humandiskused"`
	HumandiskQuota string `json:"humandiskquota"`
}

// UAPIResponse is the generic UAPI response structure
type UAPIResponse struct {
	APIVersion int              `json:"apiversion"`
	Func       string           `json:"func"`
	Module     string           `json:"module"`
	Result     *json.RawMessage `json:"result"`
	Status     int              `json:"status"`
	Errors     []string         `json:"errors"`
	Messages   []string         `json:"messages"`
}

// ListEmailsResult is the result from list_pops
type ListEmailsResult struct {
	Data []EmailAccount `json:"data"`
}

// doRequest executes an HTTP request to cPanel UAPI
func (c *Client) doRequest(module, function string, params map[string]string) ([]byte, error) {
	// Build UAPI URL
	apiURL := fmt.Sprintf("%s/execute/%s/%s", c.host, module, function)

	// Add query parameters
	if len(params) > 0 {
		values := url.Values{}
		for k, v := range params {
			values.Set(k, v)
		}
		apiURL += "?" + values.Encode()
	}

	req, err := http.NewRequest("GET", apiURL, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	// cPanel API Token authentication
	req.Header.Set("Authorization", fmt.Sprintf("cpanel %s:%s", c.username, c.apiToken))

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

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("API error (status %d): %s", resp.StatusCode, string(respBody))
	}

	return respBody, nil
}

// ListEmails returns all email accounts for a domain
func (c *Client) ListEmails(domain string) ([]EmailAccount, error) {
	params := map[string]string{}
	if domain != "" {
		params["domain"] = domain
	}

	respBody, err := c.doRequest("Email", "list_pops", params)
	if err != nil {
		return nil, err
	}

	var response UAPIResponse
	if err := json.Unmarshal(respBody, &response); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	if response.Status != 1 {
		if len(response.Errors) > 0 {
			return nil, fmt.Errorf("API error: %s", response.Errors[0])
		}
		return nil, fmt.Errorf("unknown API error")
	}

	var result []EmailAccount
	if response.Result != nil {
		if err := json.Unmarshal(*response.Result, &result); err != nil {
			return nil, fmt.Errorf("failed to parse result: %w", err)
		}
	}

	return result, nil
}

// CreateEmail creates a new email account
func (c *Client) CreateEmail(email, password string, quota int) error {
	// Parse email to get user and domain
	params := map[string]string{
		"email":    email,
		"password": password,
		"quota":    fmt.Sprintf("%d", quota), // MB, 0 = unlimited
	}

	respBody, err := c.doRequest("Email", "add_pop", params)
	if err != nil {
		return err
	}

	var response UAPIResponse
	if err := json.Unmarshal(respBody, &response); err != nil {
		return fmt.Errorf("failed to parse response: %w", err)
	}

	if response.Status != 1 {
		if len(response.Errors) > 0 {
			return fmt.Errorf("API error: %s", response.Errors[0])
		}
		return fmt.Errorf("failed to create email")
	}

	return nil
}

// DeleteEmail removes an email account
func (c *Client) DeleteEmail(email string) error {
	params := map[string]string{
		"email": email,
	}

	respBody, err := c.doRequest("Email", "delete_pop", params)
	if err != nil {
		return err
	}

	var response UAPIResponse
	if err := json.Unmarshal(respBody, &response); err != nil {
		return fmt.Errorf("failed to parse response: %w", err)
	}

	if response.Status != 1 {
		if len(response.Errors) > 0 {
			return fmt.Errorf("API error: %s", response.Errors[0])
		}
		return fmt.Errorf("failed to delete email")
	}

	return nil
}

// ChangePassword changes an email account's password
func (c *Client) ChangePassword(email, newPassword string) error {
	params := map[string]string{
		"email":    email,
		"password": newPassword,
	}

	respBody, err := c.doRequest("Email", "passwd_pop", params)
	if err != nil {
		return err
	}

	var response UAPIResponse
	if err := json.Unmarshal(respBody, &response); err != nil {
		return fmt.Errorf("failed to parse response: %w", err)
	}

	if response.Status != 1 {
		if len(response.Errors) > 0 {
			return fmt.Errorf("API error: %s", response.Errors[0])
		}
		return fmt.Errorf("failed to change password")
	}

	return nil
}

// UpdateQuota updates an email account's disk quota
func (c *Client) UpdateQuota(email string, quotaMB int) error {
	params := map[string]string{
		"email": email,
		"quota": fmt.Sprintf("%d", quotaMB),
	}

	respBody, err := c.doRequest("Email", "edit_pop_quota", params)
	if err != nil {
		return err
	}

	var response UAPIResponse
	if err := json.Unmarshal(respBody, &response); err != nil {
		return fmt.Errorf("failed to parse response: %w", err)
	}

	if response.Status != 1 {
		if len(response.Errors) > 0 {
			return fmt.Errorf("API error: %s", response.Errors[0])
		}
		return fmt.Errorf("failed to update quota")
	}

	return nil
}
