package meta

import (
	"os"

	"github.com/juju/errors"
	"gopkg.in/yaml.v3"

	"github.com/tidbcloud/auto-deploy/internal/types"
	"github.com/tidbcloud/auto-deploy/internal/utils"
	"github.com/tidbcloud/auto-deploy/internal/utils/s3"
)

func SaveRegionControl(s3uri string, region types.Region) error {
	bytes, err := s3.ReadFile(s3uri)

	if err != nil && os.IsNotExist(err) {
		bytes, err = marshalRegion(region, nil)
	} else if err != nil {
		return errors.Annotatef(err, "Failed to read meta file `%s`", s3uri)
	} else {
		bytes, err = marshalRegion(region, bytes)
	}
	if err != nil {
		return err
	}

	err = s3.WriteFile(s3uri, bytes)
	if err != nil {
		return errors.Annotatef(err, "Failed to write file %s", s3uri)
	}
	return nil
}

func LoadRegionControl(s3uri, instanceName, regionName string) (*types.Region, error) {
	bytes, err := s3.ReadFile(s3uri)
	if err != nil && os.IsNotExist(err) {
		return nil, errors.NotFound
	} else if err != nil {
		return nil, errors.Annotatef(err, "Failed to read meta file `%s`", s3uri)
	}

	return unmarshalRegion(regionName, instanceName, bytes)
}

func SaveAzureRegionControl(s3uri string, region types.Region) error {
	bytes, err := utils.ReadAzureBlobFile(s3uri)

	if err != nil && errors.Is(err, errors.NotFound) {
		bytes, err = marshalRegion(region, nil)
	} else if err != nil {
		return errors.Annotatef(err, "Failed to read meta file `%s`", s3uri)
	} else {
		bytes, err = marshalRegion(region, bytes)
	}
	if err != nil {
		return err
	}

	return utils.WriteAzureBlobFile(s3uri, bytes)
}

func SaveAliCloudRegionControl(ossUri string, region types.Region) error {
	bytes, err := utils.ReadOssFile(ossUri)

	if err != nil && errors.Is(err, errors.NotFound) {
		bytes, err = marshalRegion(region, nil)
	} else if err != nil {
		return errors.Annotatef(err, "Failed to read meta file `%s`", ossUri)
	} else {
		bytes, err = marshalRegion(region, bytes)
	}
	if err != nil {
		return err
	}

	return utils.WriteOssFile(ossUri, bytes)
}

func marshalRegion(region types.Region, oldContent []byte) ([]byte, error) {
	if region.GlobalOptions.LogicalRegionName == region.Name {
		region.GlobalOptions.LogicalRegionName = ""
	}
	if region.GlobalOptions.Env == region.InstanceName {
		region.GlobalOptions.Env = ""
	}

	var ins types.Instance
	if len(oldContent) == 0 {
		ins.Regions = make(map[string]types.Region, 1)
		ins.Regions[region.Name] = region
	} else {
		err := yaml.Unmarshal(oldContent, &ins)
		if err != nil {
			return nil, errors.Annotatef(err, "Failed to unmarshal meta")
		}
		old, found := ins.Regions[region.Name]
		if !found {
			if ins.Regions == nil {
				ins.Regions = make(map[string]types.Region, 1)
			}
			ins.Regions[region.Name] = region
		} else {
			_ = old
			ins.Regions[region.Name] = region
		}
	}

	var bytes []byte
	bytes, err := utils.MarshalYamlWithIndent(ins, 2)
	if err != nil {
		return nil, errors.Annotatef(err, "Failed to marshal meta for region %s", region.Name)
	}
	return bytes, nil
}

func LoadAzureRegionControl(s3uri, instanceName, regionName string) (*types.Region, error) {
	bytes, err := utils.ReadAzureBlobFile(s3uri)
	if err != nil && errors.Is(err, errors.NotFound) {
		return nil, err
	} else if err != nil {
		return nil, errors.Annotatef(err, "Failed to read meta file `%s`", s3uri)
	}

	return unmarshalRegion(regionName, instanceName, bytes)
}

func LoadAliCloudRegionControl(ossUri, instanceName, regionName string) (*types.Region, error) {
	bytes, err := utils.ReadOssFile(ossUri)
	if err != nil && errors.Is(err, errors.NotFound) {
		return nil, err
	} else if err != nil {
		return nil, errors.Annotatef(err, "Failed to read meta file `%s`", ossUri)
	}

	return unmarshalRegion(regionName, instanceName, bytes)
}

func unmarshalRegion(regionName string, instanceName string, bytes []byte) (*types.Region, error) {
	var ins types.Instance
	err := yaml.Unmarshal(bytes, &ins)
	if err != nil {
		return nil, errors.Annotatef(err, "Failed to unmarshal regoion")
	}
	region, ok := ins.Regions[regionName]
	if !ok {
		return nil, errors.NotFound
	}

	region.InstanceName = instanceName
	region.Name = regionName
	if region.GlobalOptions.Env == "" {
		region.GlobalOptions.Env = instanceName
	}
	if region.GlobalOptions.LogicalRegionName == "" {
		region.GlobalOptions.LogicalRegionName = regionName
	}
	region.K8s.Region = &region
	return &region, nil
}
