package service

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/spf13/viper"

	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"

	"finance-portal/pkg/log"
)

const (
	awsRegion = "us-west-2"
)

type AWSToken struct {
	AccessKeyId     string    `json:"accessKeyId"`
	SecretAccessKey string    `json:"secretAccessKey"`
	SessionToken    string    `json:"sessionToken"`
	Expiration      time.Time `json:"expiration"`
}

func getAWSClient(ctx context.Context, token *AWSToken) (*s3.Client, error) {
	cfg, err := config.LoadDefaultConfig(context.TODO(),
		config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(token.AccessKeyId, token.SecretAccessKey, token.SessionToken)),
		config.WithRegion(awsRegion),
	)
	if err != nil {
		log.L(ctx).Error("Failed to load config: " + err.Error())
		return nil, err
	}

	// 创建并返回 S3 客户端
	s3Client := s3.NewFromConfig(cfg)
	return s3Client, nil
}

type cloudRunResp struct {
	AWSToken AWSToken `json:"aws_token"`
	DownURL  string   `json:"down_url"`
}

func getTokenAndUrl(ctx context.Context) (*s3.Client, string, error) {
	log.L(ctx).Infof("getTokenAndUrl")

	cloudRunUrl := viper.GetString("cr-table.cloud-run")

	cli := http.Client{}
	req, err := http.NewRequestWithContext(ctx, "POST", cloudRunUrl, nil)
	if err != nil {
		log.L(ctx).Errorf("http.NewRequestWithContext Error", err)

		return nil, "", err
	}

	resp, err := cli.Do(req)
	if err != nil {
		log.L(ctx).Errorf("client.Do Error", err)

		return nil, "", err
	}

	if resp.StatusCode != http.StatusOK {
		log.L(ctx).Errorf("resp.StatusCode Error:%d", resp.StatusCode)

		return nil, "", fmt.Errorf("resp.StatusCode Error:%d", resp.StatusCode)

	}

	var data cloudRunResp

	err = json.NewDecoder(resp.Body).Decode(&data)

	if err != nil {
		log.L(ctx).Errorf("json.NewDecoder Error", err)

		return nil, "", err
	}

	log.L(ctx).Infof("getTokenAndUrl success")

	awsCli, err := getAWSClient(ctx, &data.AWSToken)
	if err != nil {
		log.L(ctx).Errorf("getAWSClient Error", err)

		return nil, "", err
	}

	return awsCli, data.DownURL, nil
}

func downCRTable(ctx context.Context) error {
	log.L(ctx).Infof("down cr table")

	// Get the client, URL, and bucket details
	client, url, err := getTokenAndUrl(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to get token and URL: %v", err)
		return err
	}

	bucket := viper.GetString("cr-table.bucket")

	var resp *s3.GetObjectOutput
	// Retry logic for downloading the file
	for i := 0; i < 3; i++ {
		resp, err = client.GetObject(context.TODO(), &s3.GetObjectInput{
			Bucket: &bucket,
			Key:    &url,
		})
		if err != nil {
			log.L(ctx).Errorf("client.GetObject Error (attempt %d): %v", i+1, err)
			if i < 2 {
				if i == 0 {
					time.Sleep(2 * time.Minute)
				} else {
					time.Sleep(4 * time.Minute)
				}
				continue
			}
			return err
		}
		break
	}

	if resp == nil {
		log.L(ctx).Errorf("client.GetObject returned nil response")

		return fmt.Errorf("failed to get object from S3")
	}
	defer resp.Body.Close()

	// Save the file
	savePath := viper.GetString("cr-table.path")
	fname := "cr-table" + GetLastMonth() + ".csv"
	csvFile := filepath.Join(savePath, fname)

	// Ensure the directory exists
	if err := os.MkdirAll(savePath, os.ModePerm); err != nil {
		log.L(ctx).Errorf("MkdirAll Error: %v", err)
		return err
	}

	// Create the file
	file, err := os.Create(csvFile)
	if err != nil {
		log.L(ctx).Errorf("os.Create Error: %v", err)
		return err
	}
	defer file.Close()

	// Write the content to the file
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		log.L(ctx).Errorf("io.Copy Error: %v", err)
		return err
	}

	log.L(ctx).Infof("down cr table success")
	return nil
}
