package qiniu

import (
	"context"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"u-desk/internal/oss"
)

// BucketAccessControl 空间访问控制类型
type BucketAccessControl int

const (
	// BucketAccessControlPublic 公开空间 (0)
	BucketAccessControlPublic BucketAccessControl = 0
	// BucketAccessControlPrivate 私有空间 (1)
	BucketAccessControlPrivate BucketAccessControl = 1
)

// String 返回访问控制的字符串表示
func (a BucketAccessControl) String() string {
	switch a {
	case BucketAccessControlPublic:
		return "公开"
	case BucketAccessControlPrivate:
		return "私有"
	default:
		return "未知"
	}
}

// GetBucketDomains 获取空间绑定的域名列表
// 根据: https://developer.qiniu.com/kodo/api/3949/get-the-bucket-space-domain
//
// 返回:
//   - []string: 域名列表
//   - error: 错误信息
//
// 注意：
//   - 返回的域名包括七牛云提供的默认域名和用户绑定的自定义域名
//   - 默认域名格式: <bucket>.<region>.qiniudns.com 或 <bucket>.<region>.clouddn.com
func (c *Client) GetBucketDomains(ctx context.Context) ([]string, error) {
	// 构建查询参数
	params := url.Values{}
	params.Set("tbl", c.config.Bucket)

	// 构建 URL
	// 格式: GET /v6/domain/list?tbl=<bucketName>
	apiURL := fmt.Sprintf("%s/v6/domain/list?%s", c.apiAPI, params.Encode())

	// 创建请求
	req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil)
	if err != nil {
		return nil, oss.NewError("BUCKET_ERROR", "failed to create request", err)
	}

	// 使用 API 服务的 host 生成认证
	// API 接口使用简单的查询字符串认证
	path := "/v6/domain/list"
	queryString := params.Encode()
	host := "api.qiniu.com"
	authToken := c.generateAuthTokenWithQuery("GET", path, queryString, host, "application/x-www-form-urlencoded", nil)

	req.Header.Set("Host", host)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Authorization", authToken)

	// 发送请求
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, oss.NewError("BUCKET_ERROR", "failed to get bucket domains", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, oss.NewError("BUCKET_ERROR", "failed to read response", err)
	}

	if resp.StatusCode != 200 {
		return nil, oss.NewError("BUCKET_ERROR",
			fmt.Sprintf("get bucket domains failed with status %d: %s", resp.StatusCode, string(body)), nil)
	}

	// 解析响应（JSON 数组）
	var domains []string
	if err := json.Unmarshal(body, &domains); err != nil {
		return nil, oss.NewError("BUCKET_ERROR", "failed to parse response", err)
	}

	return domains, nil
}

// SetBucketAccess 设置空间访问权限（公开/私有）
// 根据: https://developer.qiniu.com/kodo/api/3946/set-bucket-private
//
// 参数:
//   - access: BucketAccessControlPublic(公开) 或 BucketAccessControlPrivate(私有)
//
// 注意：
//   - 公开空间：文件可通过 URL 直接访问
//   - 私有空间：文件访问需要下载凭证
//   - 修改权限会影响该空间下所有文件的访问方式
func (c *Client) SetBucketAccess(ctx context.Context, access BucketAccessControl) error {
	// 构建查询参数
	params := url.Values{}
	params.Set("bucket", c.config.Bucket)
	params.Set("private", fmt.Sprintf("%d", access))

	// 构建 URL
	// 格式: POST /private?bucket=<bucketName>&private=<0|1>
	apiURL := fmt.Sprintf("%s/private?%s", c.apiAPI, params.Encode())

	// 创建请求
	req, err := http.NewRequestWithContext(ctx, "POST", apiURL, nil)
	if err != nil {
		return oss.NewError("BUCKET_ERROR", "failed to create request", err)
	}

	// 使用 API 服务的 host 生成认证
	path := "/private"
	queryString := params.Encode()
	host := "api.qiniu.com"
	authToken := c.generateAuthTokenWithQuery("POST", path, queryString, host, "application/x-www-form-urlencoded", nil)

	req.Header.Set("Host", host)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Authorization", authToken)

	// 发送请求
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return oss.NewError("BUCKET_ERROR", "failed to set bucket access", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 200 {
		return nil
	}

	body, _ := io.ReadAll(resp.Body)
	return oss.NewError("BUCKET_ERROR",
		fmt.Sprintf("set bucket access failed with status %d: %s", resp.StatusCode, string(body)), nil)
}

// SetBucketPublic 设置空间为公开空间
// 便捷方法：将空间设置为公开访问
func (c *Client) SetBucketPublic(ctx context.Context) error {
	return c.SetBucketAccess(ctx, BucketAccessControlPublic)
}

// SetBucketPrivate 设置空间为私有空间
// 便捷方法：将空间设置为私有访问
func (c *Client) SetBucketPrivate(ctx context.Context) error {
	return c.SetBucketAccess(ctx, BucketAccessControlPrivate)
}

// BucketInfo 空间信息
type BucketInfo struct {
	Name     string    // 空间名称
	Region   string    // 区域
	Domains  []string  // 绑定的域名列表
	IsPrivate bool     // 是否为私有空间
}

// GetBucketInfo 获取空间信息
// 组合方法：获取空间的域名列表和访问权限等信息
//
// 注意：
//   - 该方法会调用多个 API 接口获取完整信息
//   - IsPrivate 字段无法通过 API 直接获取，需要通过测试文件访问来确定
func (c *Client) GetBucketInfo(ctx context.Context) (*BucketInfo, error) {
	// 获取域名列表
	domains, err := c.GetBucketDomains(ctx)
	if err != nil {
		return nil, err
	}

	// 构建基本信息
	info := &BucketInfo{
		Name:    c.config.Bucket,
		Region:  c.config.Region,
		Domains: domains,
		// IsPrivate 需要通过其他方式确定
	}

	return info, nil
}

// CheckBucketAccess 检查空间访问权限
// 通过尝试访问一个不存在的文件来判断空间是否为私有
//
// 返回:
//   - bool: true=私有空间, false=公开空间
//   - error: 错误信息
//
// 注意：
//   - 该方法会发送一个测试请求来判断空间权限
//   - 如果空间内没有文件，可能无法准确判断
func (c *Client) CheckBucketAccess(ctx context.Context) (bool, error) {
	// 尝试获取一个不存在的文件的信息
	// 如果是公开空间，会返回明确的"文件不存在"错误
	// 如果是私有空间，会返回认证错误
	testKey := fmt.Sprintf("__qiniu_test_access_check_%d__", time.Now().UnixNano())

	_, err := c.GetFileInfo(ctx, testKey)
	if err == oss.ErrFileNotFound {
		// 返回文件不存在，说明是公开空间
		return false, nil
	}

	// 其他错误情况，可能需要根据错误信息判断
	if err != nil {
		// 检查错误信息中是否包含认证相关的内容
		errStr := err.Error()
		if contains(errStr, "permission") || contains(errStr, "unauthorized") || contains(errStr, "token") {
			return true, nil // 私有空间
		}
	}

	// 默认假设为公开空间
	return false, nil
}

// contains 辅助函数：检查字符串是否包含子串（忽略大小写）
func contains(str, substr string) bool {
	return len(str) >= len(substr) && (str == substr || len(str) > len(substr) && containsIgnoreCase(str, substr))
}

func containsIgnoreCase(str, substr string) bool {
	// 简化实现，实际使用时可以使用 strings.ToLower
	for i := 0; i <= len(str)-len(substr); i++ {
		match := true
		for j := 0; j < len(substr); j++ {
			c1 := str[i+j]
			c2 := substr[j]
			if c1 >= 'A' && c1 <= 'Z' {
				c1 += 32
			}
			if c2 >= 'A' && c2 <= 'Z' {
				c2 += 32
			}
			if c1 != c2 {
				match = false
				break
			}
		}
		if match {
			return true
		}
	}
	return false
}

// ListBuckets 列出所有存储桶
func ListBuckets(accessKey, secretKey string) ([]oss.BucketEntry, error) {
	signingStr := "/buckets\n"
	token := accessKey + ":" + signHmacSha1(secretKey, signingStr)

	req, _ := http.NewRequest("POST", "https://rs.qbox.me/buckets", nil)
	req.Header.Set("Authorization", "QBox "+token)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("列举存储桶失败: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("列举存储桶失败: HTTP %d: %s", resp.StatusCode, string(body))
	}

	var names []string
	if err := json.NewDecoder(resp.Body).Decode(&names); err != nil {
		return nil, fmt.Errorf("解析存储桶列表失败: %w", err)
	}

	entries := make([]oss.BucketEntry, len(names))
	for i, name := range names {
		entries[i] = oss.BucketEntry{Name: name}
	}
	return entries, nil
}

func signHmacSha1(secretKey, data string) string {
	mac := hmac.New(sha1.New, []byte(secretKey))
	mac.Write([]byte(data))
	return base64.URLEncoding.EncodeToString(mac.Sum(nil))
}
