package cloudflare

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

	"github.com/pkg/errors"
)

// Client represents a Cloudflare API client
type Client struct {
	apiToken  string
	accountID string
	httpClient *http.Client
	baseURL   string
}

// NewClient creates a new Cloudflare API client
func NewClient(apiToken, accountID string) *Client {
	return &Client{
		apiToken:  apiToken,
		accountID: accountID,
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
		baseURL: "https://api.cloudflare.com/client/v4",
	}
}

// makeRequest makes an HTTP request to the Cloudflare API
func (c *Client) makeRequest(ctx context.Context, method, path string, body interface{}) (*http.Response, error) {
	var reqBody io.Reader
	if body != nil {
		jsonData, err := json.Marshal(body)
		if err != nil {
			return nil, errors.Wrap(err, "failed to marshal request body")
		}
		reqBody = bytes.NewBuffer(jsonData)
	}

	url := c.baseURL + path
	req, err := http.NewRequestWithContext(ctx, method, url, reqBody)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create request")
	}

	req.Header.Set("Authorization", "Bearer "+c.apiToken)
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "request failed")
	}

	return resp, nil
}

// parseResponse parses the Cloudflare API response
func (c *Client) parseResponse(resp *http.Response, result interface{}) error {
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		bodyBytes, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("API error: %d %s - %s", resp.StatusCode, resp.Status, string(bodyBytes))
	}

	if result != nil {
		if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
			return errors.Wrap(err, "failed to decode response")
		}
	}

	return nil
}

// Tunnel represents a Cloudflare Tunnel
type Tunnel struct {
	ID          string
	Name        string
	Status      string
	Connections int
	CreatedAt   time.Time
}

// DNSRecord represents a DNS record
type DNSRecord struct {
	ID       string
	Type     string
	Name     string
	Content  string
	TTL      int
	ZoneID   string
	ZoneName string
}

// ZeroTrustPolicy represents a Zero Trust access policy
type ZeroTrustPolicy struct {
	ID          string
	Name        string
	Decision    string
	Include     []map[string]interface{}
	Exclude     []map[string]interface{}
	Require     []map[string]interface{}
}

// ListTunnels lists all Cloudflare Tunnels
func (c *Client) ListTunnels(ctx context.Context) ([]Tunnel, error) {
	resp, err := c.makeRequest(ctx, "GET", fmt.Sprintf("/accounts/%s/cfd_tunnel", c.accountID), nil)
	if err != nil {
		return nil, err
	}

	var apiResponse struct {
		Success bool `json:"success"`
		Result  []struct {
			ID          string    `json:"id"`
			Name        string    `json:"name"`
			Status      string    `json:"status"`
			Connections int       `json:"connections"`
			CreatedAt   time.Time `json:"created_at"`
		} `json:"result"`
		Errors []interface{} `json:"errors"`
	}

	if err := c.parseResponse(resp, &apiResponse); err != nil {
		return nil, err
	}

	if !apiResponse.Success {
		return nil, fmt.Errorf("API returned success=false")
	}

	tunnels := make([]Tunnel, len(apiResponse.Result))
	for i, t := range apiResponse.Result {
		tunnels[i] = Tunnel{
			ID:          t.ID,
			Name:        t.Name,
			Status:      t.Status,
			Connections: t.Connections,
			CreatedAt:   t.CreatedAt,
		}
	}

	return tunnels, nil
}

// ListDNSRecords lists DNS records for a zone
func (c *Client) ListDNSRecords(ctx context.Context, zoneID string) ([]DNSRecord, error) {
	resp, err := c.makeRequest(ctx, "GET", fmt.Sprintf("/zones/%s/dns_records", zoneID), nil)
	if err != nil {
		return nil, err
	}

	var apiResponse struct {
		Success bool `json:"success"`
		Result  []struct {
			ID       string `json:"id"`
			Type     string `json:"type"`
			Name     string `json:"name"`
			Content  string `json:"content"`
			TTL      int    `json:"ttl"`
			ZoneID   string `json:"zone_id"`
			ZoneName string `json:"zone_name"`
		} `json:"result"`
		Errors []interface{} `json:"errors"`
	}

	if err := c.parseResponse(resp, &apiResponse); err != nil {
		return nil, err
	}

	if !apiResponse.Success {
		return nil, fmt.Errorf("API returned success=false")
	}

	records := make([]DNSRecord, len(apiResponse.Result))
	for i, r := range apiResponse.Result {
		records[i] = DNSRecord{
			ID:       r.ID,
			Type:     r.Type,
			Name:     r.Name,
			Content:  r.Content,
			TTL:      r.TTL,
			ZoneID:   r.ZoneID,
			ZoneName: r.ZoneName,
		}
	}

	return records, nil
}

// ListZones lists all DNS zones
func (c *Client) ListZones(ctx context.Context) ([]string, error) {
	resp, err := c.makeRequest(ctx, "GET", "/zones", nil)
	if err != nil {
		return nil, err
	}

	var apiResponse struct {
		Success bool `json:"success"`
		Result  []struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		} `json:"result"`
		Errors []interface{} `json:"errors"`
	}

	if err := c.parseResponse(resp, &apiResponse); err != nil {
		return nil, err
	}

	if !apiResponse.Success {
		return nil, fmt.Errorf("API returned success=false")
	}

	zoneIDs := make([]string, len(apiResponse.Result))
	for i, z := range apiResponse.Result {
		zoneIDs[i] = z.ID
	}

	return zoneIDs, nil
}

// ListZeroTrustPolicies lists Zero Trust access policies
func (c *Client) ListZeroTrustPolicies(ctx context.Context) ([]ZeroTrustPolicy, error) {
	resp, err := c.makeRequest(ctx, "GET", fmt.Sprintf("/accounts/%s/access/policies", c.accountID), nil)
	if err != nil {
		return nil, err
	}

	var apiResponse struct {
		Success bool `json:"success"`
		Result  []struct {
			ID       string                   `json:"id"`
			Name     string                   `json:"name"`
			Decision string                   `json:"decision"`
			Include  []map[string]interface{} `json:"include"`
			Exclude  []map[string]interface{} `json:"exclude"`
			Require  []map[string]interface{} `json:"require"`
		} `json:"result"`
		Errors []interface{} `json:"errors"`
	}

	if err := c.parseResponse(resp, &apiResponse); err != nil {
		return nil, err
	}

	if !apiResponse.Success {
		return nil, fmt.Errorf("API returned success=false")
	}

	policies := make([]ZeroTrustPolicy, len(apiResponse.Result))
	for i, p := range apiResponse.Result {
		policies[i] = ZeroTrustPolicy{
			ID:       p.ID,
			Name:     p.Name,
			Decision: p.Decision,
			Include:  p.Include,
			Exclude:  p.Exclude,
			Require:  p.Require,
		}
	}

	return policies, nil
}

