package service

import (
	"context"
	"encoding/csv"
	"os"
	"strconv"
	"time"

	"github.com/spf13/viper"
	"github.com/tidwall/gjson"
	"google.golang.org/api/option"

	"google.golang.org/api/storage/v1"

	"finance-portal/model"
	"finance-portal/pkg/errors"
	"finance-portal/pkg/log"
)

func uploadARTable(ctx context.Context, records []model.ARTable) error {
	log.L(ctx).Info("start uploadARTable")

	savePath := viper.GetString("cr-table.save-path")
	// 创建指定路径的 CSV 文件
	file, err := os.Create(savePath)
	if err != nil {
		log.L(ctx).Errorf("error creating CSV file: %v", err)
		return err
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	// 写入 CSV 头
	headers, err := arHeaders()
	if err != nil {
		log.L(ctx).Errorf("error arHeaders: %v", err)

		return err
	}
	if err := writer.Write(headers); err != nil {
		log.L(ctx).Errorf("error writing CSV headers: %v", err)
		return err
	}

	// 写入数据
	for _, record := range records {
		row := strRecord(record) // 根据你的 ARRecord 结构定义 CSV 行
		if err := writer.Write(row); err != nil {
			log.L(ctx).Errorf("error writing CSV row: %v", err)
			return err
		}
	}

	// 确保所有数据都写入文件
	writer.Flush()
	if err := writer.Error(); err != nil {
		log.L(ctx).Errorf("error flushing CSV writer: %v", err)
		return err
	}

	// 将文件指针重置到文件开头
	if _, err := file.Seek(0, 0); err != nil {
		log.L(ctx).Errorf("error seeking file: %v", err)
		return err
	}

	// 上传到 GCS
	mode := viper.GetString("mode")

	var gcsService *storage.Service
	if mode == "dev" {
		saPath := viper.GetString("bq.sa")
		gcsService, err = storage.NewService(ctx, option.WithCredentialsFile(saPath))
	} else {
		gcsService, err = storage.NewService(ctx)
	}
	if err != nil {
		log.L(ctx).Errorf("error creating GCS client: %v", err)

		return err
	}

	arGcsPath := viper.GetString("gcs.arPath")
	_, err = gcsService.Objects.Insert(dataPlatformBucket, &storage.Object{
		Name: arGcsPath,
	}).Media(file).Do()
	if err != nil {
		log.L(ctx).Errorf("error uploading file to GCS: %v", err)

		return err
	}

	log.L(ctx).Info("end uploadARTable")

	return nil
}

func arHeaders() ([]string, error) {
	var headers []string
	schemaPath := viper.GetString("schema")

	// 检查 schemaPath 是否为空
	if schemaPath == "" {
		return nil, errors.New("schema path is empty")
	}

	// 读取 JSON 文件
	data, err := os.ReadFile(schemaPath)
	if err != nil {
		return nil, err
	}

	// 使用 gjson 解析 JSON
	result := gjson.ParseBytes(data)
	result.ForEach(func(key, value gjson.Result) bool {
		headers = append(headers, key.String())
		return true // 继续遍历
	})

	return headers, nil
}

func strRecord(record model.ARTable) []string {
	res := []string{
		safeDereferenceString(record.BillMonth),
		safeFormatDate(record.StartDate),
		safeFormatDate(record.EndDate),
		safeDereferenceString(record.TenantID),
		safeDereferenceString(record.StripeAmount),
		safeDereferenceString(record.StripeTaxRate),
		safeDereferenceString(record.StripeAmountIncludingTax),
		safeDereferenceString(record.StripeInvoiceNumber),
		safeDereferenceFloat(record.WireTransferAmount),
		safeDereferenceFloat(record.MarketplaceAmount),
		safeDereferenceFloat(record.PrepaymentCredits),
		safeDereferenceFloat(record.TotalAmount),
		safeDereferenceFloat(record.InvoiceAmount),
		safeDereferenceInt(record.NumberOfSegments),
		safeFormatDate(record.SegmentAStartDate),
		safeFormatDate(record.SegmentAEndDate),
		safeDereferenceFloat(record.SegmentAAmount),
		safeFormatDate(record.SegmentBStartDate),
		safeFormatDate(record.SegmentBEndDate),
		safeDereferenceFloat(record.SegmentBAmount),
		safeFormatDate(record.SegmentCStartDate),
		safeFormatDate(record.SegmentCEndDate),
		safeDereferenceFloat(record.SegmentCAmount),
		safeDereferenceString(record.StripeInvoice),
		safeDereferenceString(record.StripeInvoicePaid),
		safeDereferenceString(record.StripeInvoiceFinalizedDate),
		safeDereferenceString(record.StripeInvoicePaidDate),
		safeDereferenceFloat(record.RawStarterAmount),
		safeDereferenceFloat(record.RawDedicatedAmount),
		safeDereferenceFloat(record.RawSupportAmount),
		safeDereferenceFloat(record.RawByocAmount),
		safeDereferenceFloat(record.ChannelLossAmount),
		safeDereferenceFloat(record.StarterAllocatedLoss),
		safeDereferenceFloat(record.DedicatedAllocatedLoss),
		safeDereferenceFloat(record.SupportAllocatedLoss),
		safeDereferenceFloat(record.RawProgramCreditAmountStarter),
		safeDereferenceFloat(record.RawRedeemCreditAmountSupport),
		safeDereferenceFloat(record.RawRedeemCreditAmountDedicated),
		safeDereferenceFloat(record.RawProgramCreditAmountDedicated),
		safeDereferenceFloat(record.RawRedeemCreditAmountStarter),
		safeDereferenceFloat(record.RawCustomerSuccessCreditAmountStarter),
		safeDereferenceFloat(record.RawCustomerSuccessCreditAmountSupport),
		safeDereferenceFloat(record.RawProgramCreditAmountSupport),
		safeDereferenceFloat(record.RawCustomerSuccessCreditAmountDedicated),
		safeDereferenceFloat(record.RawProgramCreditAmountByoc),
		safeDereferenceFloat(record.RawRedeemCreditAmountByoc),
		safeDereferenceFloat(record.RawCustomerSuccessCreditAmountByoc),
	}

	return res
}

// Helper functions for safe dereferencing
func safeDereferenceString(s *string) string {
	if s != nil {
		return *s
	}
	return ""
}

func safeDereferenceFloat(f *float64) string {
	if f != nil {
		return strconv.FormatFloat(*f, 'f', -1, 64)
	}
	return ""
}

func safeDereferenceInt(i *int) string {
	if i != nil {
		return strconv.Itoa(*i)
	}
	return ""
}

func safeFormatDate(date *time.Time) string {
	if date != nil {
		return date.Format("2006-01-02")
	}
	return ""
}
