package export

import (
	"google.golang.org/protobuf/types/known/timestamppb"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/cloudstorage"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
)

func schemaToProto(s *schema.Export) *globalngv1.Export {
	out := &globalngv1.Export{
		ExportId:    s.ExportId,
		DisplayName: s.DisplayName,
		TidbId:      s.ClusterId,
		CreatedBy:   s.CreatedBy,
		State:       schemaExportStateToProto(s.State),
		Reason:      s.Reason,
		SnapshotTso: s.SnapshotTso,
		Username:    s.Username,
		CreateTime:  timestamppb.New(s.CreatedAt),
	}
	if !s.UpdatedAt.IsZero() {
		out.UpdateTime = timestamppb.New(s.UpdatedAt)
	}
	if s.CompleteTime != nil {
		out.CompleteTime = timestamppb.New(*s.CompleteTime)
	}
	if s.SnapshotTime != nil {
		out.SnapshotTime = timestamppb.New(*s.SnapshotTime)
	}
	if s.ExportOptions != nil {
		out.ExportOptions = schemaExportOptionsToProto(s.ExportOptions)
	}
	if s.CloudStorage != nil {
		out.CloudStorage = cloudstorage.SchemaToProto(s.CloudStorage)
	}
	return out
}

func schemaExportStateToProto(st schema.ExportState) globalngv1.Export_State {
	switch st {
	case schema.ExportStateRunning:
		return globalngv1.Export_RUNNING
	case schema.ExportStateSucceeded:
		return globalngv1.Export_SUCCEEDED
	case schema.ExportStateFailed:
		return globalngv1.Export_FAILED
	case schema.ExportStateCanceled:
		return globalngv1.Export_CANCELED
	case schema.ExportStateDeleted:
		return globalngv1.Export_DELETED
	default:
		return globalngv1.Export_STATE_UNSPECIFIED
	}
}
func schemaExportOptionsToProto(m *schema.ExportOptions) *globalngv1.ExportOptions {
	if m == nil {
		return nil
	}
	out := &globalngv1.ExportOptions{
		FileType:        globalngv1.ExportOptions_FileType(globalngv1.ExportOptions_FileType_value[m.FileType]),
		Compression:     globalngv1.ExportOptions_CompressionType(globalngv1.ExportOptions_CompressionType_value[m.Compression]),
		EscapeBackslash: m.EscapeBackslash,
	}
	if m.Filter != nil {
		out.Filter = schemaExportOptionsFilterToProto(m.Filter)
	}
	if m.CSVFormat != nil {
		out.CsvFormat = schemaCSVFormatToProto(m.CSVFormat)
	}
	if m.ParquetFormat != nil {
		out.ParquetFormat = schemaParquetFormatToProto(m.ParquetFormat)
	}
	return out
}

func schemaExportOptionsFilterToProto(m *schema.ExportOptionsFilter) *globalngv1.ExportOptions_Filter {
	if m == nil {
		return nil
	}
	out := &globalngv1.ExportOptions_Filter{}
	if m.Sql != nil {
		out.Type = &globalngv1.ExportOptions_Filter_Sql{Sql: *m.Sql}
		return out
	}
	if m.Table != nil {
		out.Type = &globalngv1.ExportOptions_Filter_Table_{
			Table: &globalngv1.ExportOptions_Filter_Table{
				Patterns:   m.Table.Patterns,
				Where:      m.Table.Where,
				Partitions: m.Table.Partitions,
			},
		}
		return out
	}
	return nil
}

func schemaParquetFormatToProto(m *schema.ExportOptionsParquetFormat) *globalngv1.ExportOptions_ParquetFormat {
	if m == nil {
		return nil
	}
	return &globalngv1.ExportOptions_ParquetFormat{
		Compression: globalngv1.ExportParquetCompressionType_Enum(
			globalngv1.ExportParquetCompressionType_Enum_value[m.Compression],
		),
	}
}

func schemaCSVFormatToProto(m *schema.ExportOptionsCSVFormat) *globalngv1.ExportOptions_CSVFormat {
	if m == nil {
		return nil
	}
	out := &globalngv1.ExportOptions_CSVFormat{
		Separator:  m.Separator,
		SkipHeader: m.SkipHeader,
		Delimiter:  m.Delimiter,
		NullValue:  m.NullValue,
	}
	if m.Dialect != nil {
		if value, ok := globalngv1.ExportOptions_ExportCSVDialect_Enum_value[*m.Dialect]; ok {
			dialect := globalngv1.ExportOptions_ExportCSVDialect_Enum(value)
			out.Dialect = &dialect
		}
	}
	return out
}

func protoToSchema(exp *globalngv1.Export) *schema.Export {
	if exp == nil {
		return nil
	}
	s := &schema.Export{
		ExportId:          exp.GetExportId(),
		DisplayName:       exp.GetDisplayName(),
		CreatedBy:         exp.GetCreatedBy(),
		Reason:            exp.GetReason(),
		SnapshotTso:       exp.GetSnapshotTso(),
		Username:          exp.GetUsername(),
		EncryptedPassword: exp.GetPassword(),
		ExportOptions:     protoExportOptionsToSchema(exp.GetExportOptions()),
		CloudStorage:      cloudstorage.ProtoToSchema(exp.GetCloudStorage()),
	}
	return s
}

func protoExportOptionsToSchema(opts *globalngv1.ExportOptions) *schema.ExportOptions {
	if opts == nil {
		return nil
	}
	out := &schema.ExportOptions{
		FileType:        opts.GetFileType().String(),
		Compression:     opts.GetCompression().String(),
		EscapeBackslash: opts.EscapeBackslash,
	}
	if f := opts.GetFilter(); f != nil {
		out.Filter = protoExportOptionsFilterToSchema(f)
	}
	if opts.GetCsvFormat() != nil {
		out.CSVFormat = protoCSVFormatToSchema(opts.GetCsvFormat())
	}
	if opts.GetParquetFormat() != nil {
		out.ParquetFormat = protoParquetFormatToSchema(opts.GetParquetFormat())
	}
	return out
}

func protoExportOptionsFilterToSchema(f *globalngv1.ExportOptions_Filter) *schema.ExportOptionsFilter {
	if f == nil {
		return nil
	}
	out := &schema.ExportOptionsFilter{}
	if s := f.GetSql(); s != "" {
		out.Sql = &s
		return out
	}
	if t := f.GetTable(); t != nil {
		out.Table = &schema.ExportOptionsFilterTable{
			Patterns:   t.GetPatterns(),
			Where:      t.GetWhere(),
			Partitions: t.GetPartitions(),
		}
		return out
	}
	return nil
}

func protoParquetFormatToSchema(p *globalngv1.ExportOptions_ParquetFormat) *schema.ExportOptionsParquetFormat {
	if p == nil {
		return nil
	}
	out := &schema.ExportOptionsParquetFormat{}
	if p.GetCompression() != globalngv1.ExportParquetCompressionType_PARQUET_COMPRESSION_TYPE_UNSPECIFIED {
		out.Compression = p.GetCompression().String()
	}
	return out
}

func protoCSVFormatToSchema(c *globalngv1.ExportOptions_CSVFormat) *schema.ExportOptionsCSVFormat {
	if c == nil {
		return nil
	}
	out := &schema.ExportOptionsCSVFormat{
		Separator:  c.Separator,
		Delimiter:  c.Delimiter,
		NullValue:  c.NullValue,
		SkipHeader: c.SkipHeader,
	}
	if c.Dialect != nil {
		dialect := c.GetDialect().String()
		out.Dialect = &dialect
	}
	return out
}
