package catalogreconcile

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/nexuscrm/shared/pkg/models"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	consumerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/catalogapp"
	"github.com/pingcap-inc/nexuscrm-controlplane/componentbehavior"
	"github.com/pingcap-inc/nexuscrm-controlplane/layoutownership"
)

const appReleasePageLayoutComponentKind = "metadata.pack.page-layout.v1"

type EffectivePageLayoutMetadataReader interface {
	GetLayouts(context.Context, string) ([]*models.PageLayout, error)
}

func canonicalPageLayoutModel(layout *models.PageLayout) ([]byte, error) {
	if layout == nil {
		return nil, fmt.Errorf("effective page layout is required")
	}
	value := *layout
	value.ID = ""
	value.IsDeleted = false
	value.IsCustomized = false
	value.LayoutVersion = 0
	value.Source = ""
	value.GenerationVersion = 0
	value.CreatedDate = time.Time{}
	value.LastModifiedDate = time.Time{}
	encoded, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}
	return componentbehavior.CanonicalJSONV1(encoded)
}

func canonicalPageLayoutBehavior(source []byte) ([]byte, error) {
	layout, err := layoutownership.DecodePageLayoutConfigStrict(source)
	if err != nil {
		return nil, err
	}
	return canonicalPageLayoutModel(&layout)
}

func pageLayoutObjectFromBusinessKey(value string) (string, error) {
	objectName, suffix, ok := strings.Cut(strings.TrimSpace(value), "/")
	if !ok || objectName == "" || suffix != "default" || strings.Contains(objectName, "/") {
		return "", fmt.Errorf("page layout business key %q is not object/default", value)
	}
	return objectName, nil
}

func effectiveDefaultPageLayout(
	layouts []*models.PageLayout,
	objectName string,
) (*models.PageLayout, error) {
	var selected *models.PageLayout
	for index, layout := range layouts {
		if layout == nil {
			return nil, fmt.Errorf("effective page layout %q[%d] is nil", objectName, index)
		}
		if !strings.EqualFold(strings.TrimSpace(layout.ObjectAPIName), objectName) {
			return nil, fmt.Errorf("effective page layout %q contains foreign object %q", objectName, layout.ObjectAPIName)
		}
		if !layout.IsDefault || layout.IsDeleted {
			continue
		}
		if selected != nil {
			return nil, fmt.Errorf("effective page layout %q has multiple defaults", objectName)
		}
		selected = layout
	}
	if selected == nil {
		return nil, fmt.Errorf("effective page layout %q has no default", objectName)
	}
	return selected, nil
}

func buildEffectivePageLayoutComponents(
	ctx context.Context,
	reader EffectivePageLayoutMetadataReader,
	release *appauthority.VerifiedPackage,
	objectNames []string,
) ([]catalogapp.EffectiveMetadataComponent, error) {
	if reader == nil || release == nil {
		return nil, fmt.Errorf("effective page-layout publication requires reader and verified AppRelease")
	}
	verified, err := appauthority.VerifyPackage(release.Package)
	if err != nil {
		return nil, fmt.Errorf("verify effective page-layout AppRelease: %w", err)
	}
	if verified.AppReleaseDigest != release.AppReleaseDigest {
		return nil, fmt.Errorf("effective page-layout AppRelease identity disagrees with its verified package")
	}

	result := make([]catalogapp.EffectiveMetadataComponent, 0)
	releaseLayoutObjects := make(map[string]struct{})
	// Base-backed layouts retain their AppRelease comparison and override-policy
	// contract. An unchanged base remains in AppComponents and needs no effective
	// projection.
	for index, component := range verified.Bundle.Components {
		if component.ComponentKind != appReleasePageLayoutComponentKind {
			continue
		}
		if component.ComponentFormatVersion != 1 {
			return nil, fmt.Errorf("page layout component %q has unsupported format version %d", component.ComponentID, component.ComponentFormatVersion)
		}
		objectName, err := pageLayoutObjectFromBusinessKey(component.BusinessKey)
		if err != nil {
			return nil, err
		}
		objectKey := strings.ToLower(objectName)
		if _, duplicate := releaseLayoutObjects[objectKey]; duplicate {
			return nil, fmt.Errorf("AppRelease contains multiple default page layouts for %q", objectName)
		}
		releaseLayoutObjects[objectKey] = struct{}{}
		layouts, err := reader.GetLayouts(ctx, objectName)
		if err != nil {
			return nil, fmt.Errorf("read effective page layouts for %q: %w", objectName, err)
		}
		effective, err := effectiveDefaultPageLayout(layouts, objectName)
		if err != nil {
			return nil, err
		}
		effectiveBehavior, err := canonicalPageLayoutModel(effective)
		if err != nil {
			return nil, fmt.Errorf("encode effective page layout %q: %w", component.BusinessKey, err)
		}
		appBehavior, err := canonicalPageLayoutBehavior(
			verified.ComponentBlobs[index].CanonicalBehavior,
		)
		if err != nil {
			return nil, fmt.Errorf("read AppRelease page layout %q: %w", component.BusinessKey, err)
		}
		if bytes.Equal(effectiveBehavior, appBehavior) {
			continue
		}
		if component.OverridePolicy != appauthority.OverridePolicyPreserveAuthorized {
			return nil, fmt.Errorf("page layout component %q does not authorize an effective override", component.BusinessKey)
		}
		result = append(result, catalogapp.EffectiveMetadataComponent{
			ComponentKind:          consumerv1.EffectivePageLayoutComponentKind,
			ComponentFormatVersion: 1,
			BusinessKey:            component.BusinessKey,
			ContentDigest: consumerv1.ComputeEffectiveMetadataComponentDigest(
				effectiveBehavior,
			),
			CanonicalBehavior: effectiveBehavior,
		})
	}

	// An effective object may be introduced after the L1 AppRelease by a tenant
	// pack. Publish its runtime default as an effective-only component; objects
	// with no stored layout have no page-layout identity to publish.
	seenRuntimeObjects := make(map[string]struct{}, len(objectNames))
	for index, objectName := range objectNames {
		objectName = strings.TrimSpace(objectName)
		if objectName == "" || strings.Contains(objectName, "/") {
			return nil, fmt.Errorf("effective object metadata[%d] has invalid page-layout identity %q", index, objectName)
		}
		objectKey := strings.ToLower(objectName)
		if _, duplicate := seenRuntimeObjects[objectKey]; duplicate {
			return nil, fmt.Errorf("effective object metadata contains duplicate page-layout identity %q", objectName)
		}
		seenRuntimeObjects[objectKey] = struct{}{}
		if _, baseBacked := releaseLayoutObjects[objectKey]; baseBacked {
			continue
		}
		layouts, err := reader.GetLayouts(ctx, objectName)
		if err != nil {
			return nil, fmt.Errorf("read effective page layouts for %q: %w", objectName, err)
		}
		if len(layouts) == 0 {
			continue
		}
		effective, err := effectiveDefaultPageLayout(layouts, objectName)
		if err != nil {
			return nil, err
		}
		effectiveBehavior, err := canonicalPageLayoutModel(effective)
		if err != nil {
			return nil, fmt.Errorf("encode effective page layout %q: %w", objectName+"/default", err)
		}
		result = append(result, catalogapp.EffectiveMetadataComponent{
			ComponentKind:          consumerv1.EffectivePageLayoutComponentKind,
			ComponentFormatVersion: 1,
			BusinessKey:            objectName + "/default",
			ContentDigest: consumerv1.ComputeEffectiveMetadataComponentDigest(
				effectiveBehavior,
			),
			CanonicalBehavior: effectiveBehavior,
		})
	}
	sort.Slice(result, func(left, right int) bool {
		return result[left].BusinessKey < result[right].BusinessKey
	})
	return result, nil
}
