package awshandler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"go.uber.org/zap"
)

type ARTableExportMarker struct {
	RunID     string    `json:"run_id"`
	StartedAt time.Time `json:"started_at"`
	TimeoutAt time.Time `json:"timeout_at"`
	Year      int       `json:"year"`
	Month     int       `json:"month"`
	Type      string    `json:"type"`
}

func BuildS3ARTableKey(date time.Time, arType string) string {
	env := config.Get().GetString(config.ENV)
	year := date.Format("2006")
	month := date.Format("01")
	return fmt.Sprintf("%s/%s/%s/%s-%s-%s-%s.csv", env, year, month, env, year, month, arType)
}

func BuildS3ARTableRunningKey(date time.Time, arType string) string {
	env := config.Get().GetString(config.ENV)
	year := date.Format("2006")
	month := date.Format("01")
	return fmt.Sprintf("%s/%s/%s/%s-%s-%s-%s.running.json", env, year, month, env, year, month, arType)
}

func PutARTableToS3(key string, data []byte) error {
	if !config.Get().GetBool(config.EnablePutARTableToS3) {
		log.Info("not enable put ar table to s3")
		return nil
	}
	bucketName := config.Get().GetString(config.ARTableAWSBucketName)
	s3Cli, err := NewS3Client()
	if err != nil {
		return err
	}
	_, err = s3Cli.PutObject(&s3.PutObjectInput{
		Bucket:      &bucketName,
		Key:         &key,
		Body:        bytes.NewReader(data),
		ContentType: aws.String("text/csv"),
	})
	if err != nil {
		log.Info("failed to put AR Table to S3", zap.Error(err), zap.String("key", key))
		return err
	}
	log.Info("success to put AR Table to S3", zap.String("key", key))
	return nil
}

func PutARTableRunningMarker(key string, marker *ARTableExportMarker) error {
	if !config.Get().GetBool(config.EnablePutARTableToS3) {
		log.Info("not enable put ar table to s3")
		return nil
	}
	data, err := json.Marshal(marker)
	if err != nil {
		return err
	}
	bucketName := config.Get().GetString(config.ARTableAWSBucketName)
	s3Cli, err := NewS3Client()
	if err != nil {
		return err
	}
	_, err = s3Cli.PutObject(&s3.PutObjectInput{
		Bucket:      &bucketName,
		Key:         &key,
		Body:        bytes.NewReader(data),
		ContentType: aws.String("application/json"),
	})
	if err != nil {
		log.Info("failed to put AR table running marker to S3", zap.Error(err), zap.String("key", key))
		return err
	}
	return nil
}

func GetARTableRunningMarker(key string) (*ARTableExportMarker, bool, error) {
	bucketName := config.Get().GetString(config.ARTableAWSBucketName)
	s3Cli, err := NewS3Client()
	if err != nil {
		return nil, false, err
	}
	resp, err := s3Cli.GetObject(&s3.GetObjectInput{
		Bucket: &bucketName,
		Key:    &key,
	})
	if err != nil {
		if isS3NotFound(err) {
			return nil, false, nil
		}
		return nil, false, err
	}
	defer resp.Body.Close()
	marker := &ARTableExportMarker{}
	if err := json.NewDecoder(resp.Body).Decode(marker); err != nil {
		return nil, false, err
	}
	return marker, true, nil
}

func DeleteARTableRunningMarker(key string) error {
	bucketName := config.Get().GetString(config.ARTableAWSBucketName)
	s3Cli, err := NewS3Client()
	if err != nil {
		return err
	}
	_, err = s3Cli.DeleteObject(&s3.DeleteObjectInput{
		Bucket: &bucketName,
		Key:    &key,
	})
	if err != nil {
		return err
	}
	return nil
}

func GetARTableObjectLastModified(key string) (*time.Time, bool, error) {
	bucketName := config.Get().GetString(config.ARTableAWSBucketName)
	s3Cli, err := NewS3Client()
	if err != nil {
		return nil, false, err
	}
	resp, err := s3Cli.HeadObject(&s3.HeadObjectInput{
		Bucket: &bucketName,
		Key:    &key,
	})
	if err != nil {
		if isS3NotFound(err) {
			return nil, false, nil
		}
		return nil, false, err
	}
	return resp.LastModified, true, nil
}

func CheckS3ARTableExists(key string) (bool, error) {
	_, exists, err := GetARTableObjectLastModified(key)
	return exists, err
}

func GetARTablePresignURL(key string) (string, error) {
	bucketName := config.Get().GetString(config.ARTableAWSBucketName)
	expireMinutes := config.Get().GetInt(config.ARTablePresignExpireMinutes)
	s3Cli, err := NewS3Client()
	if err != nil {
		return "", err
	}
	req, _ := s3Cli.GetObjectRequest(&s3.GetObjectInput{
		Bucket: &bucketName,
		Key:    &key,
	})
	urlStr, err := req.Presign(time.Duration(expireMinutes) * time.Minute)
	if err != nil {
		return "", err
	}
	return urlStr, nil
}
