package persistence

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	apperrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/constants"
)

type adminFieldAdmissionState struct {
	id           string
	ownerKind    string
	ownerPack    string
	isCustomized bool
	isDeleted    bool
}

// AdmitAdminFieldMetadataWrite applies the same live owner-tuple guard used by
// SaveFieldMetadataWithIDs, using the caller's active transaction executor.
func (r *SchemaRepository) AdmitAdminFieldMetadataWrite(ctx context.Context, objectID, objectAPIName, fieldAPIName string, tx interface{}) error {
	exec, ok := tx.(Executor)
	if !ok || exec == nil {
		return apperrors.NewInternalError("admin field metadata admission requires an active transaction executor", nil)
	}
	if _, rootDB := tx.(*sql.DB); rootDB {
		return apperrors.NewInternalError("admin field metadata admission rejects the root database executor", nil)
	}
	return r.admitAdminFieldMetadataWriteByObjectID(ctx, exec, objectID, objectAPIName, fieldAPIName)
}

func (r *SchemaRepository) admitAdminFieldMetadataWriteByObjectID(ctx context.Context, exec Executor, objectID, objectAPIName, fieldAPIName string) error {
	state, found, err := r.loadAdminFieldAdmissionStateByObjectID(ctx, exec, objectID, fieldAPIName)
	if err != nil {
		return err
	}
	if !found {
		return nil
	}
	return validateAdminFieldMetadataAdmissionState(objectAPIName, fieldAPIName, state)
}

func (r *SchemaRepository) admitAdminFieldPhysicalWriteByObjectAPI(ctx context.Context, objectAPIName, fieldAPIName string, requireExisting bool) error {
	state, found, err := r.loadAdminFieldAdmissionStateByObjectAPI(ctx, objectAPIName, fieldAPIName)
	if err != nil {
		return err
	}
	if !found {
		if requireExisting {
			return apperrors.NewValidationError("OwnerKind", fmt.Sprintf("field %s has no _System_Field owner tuple; refusing admin field write", formatFieldAdmissionKey(objectAPIName, fieldAPIName)))
		}
		return nil
	}
	return validateAdminFieldPhysicalAdmissionState(objectAPIName, fieldAPIName, state)
}

func (r *SchemaRepository) loadAdminFieldAdmissionStateByObjectID(ctx context.Context, exec Executor, objectID, fieldAPIName string) (adminFieldAdmissionState, bool, error) {
	if exec == nil {
		exec = r.db
	}
	var state adminFieldAdmissionState
	var isCustomized int
	var isDeleted int
	err := exec.QueryRowContext(ctx,
		"SELECT f.Id, COALESCE(f.OwnerKind, ''), COALESCE(f.OwnerPack, ''), COALESCE(f.IsCustomized, 0), COALESCE(f.IsDeleted, 0) FROM `_System_Field` f WHERE f.ObjectId = ? AND f.ApiName = ? LIMIT 1",
		objectID,
		fieldAPIName,
	).Scan(&state.id, &state.ownerKind, &state.ownerPack, &isCustomized, &isDeleted)
	if err == sql.ErrNoRows {
		return adminFieldAdmissionState{}, false, nil
	}
	if err != nil {
		return adminFieldAdmissionState{}, false, apperrors.NewInternalError(fmt.Sprintf("failed to load field owner tuple for %s", fieldAPIName), err)
	}
	normalizeAdminFieldAdmissionState(&state)
	state.isCustomized = isCustomized != 0
	state.isDeleted = isDeleted != 0
	return state, true, nil
}

func (r *SchemaRepository) loadAdminFieldAdmissionStateByObjectAPI(ctx context.Context, objectAPIName, fieldAPIName string) (adminFieldAdmissionState, bool, error) {
	var state adminFieldAdmissionState
	var isCustomized int
	var isDeleted int
	err := r.db.QueryRowContext(ctx,
		"SELECT f.Id, COALESCE(f.OwnerKind, ''), COALESCE(f.OwnerPack, ''), COALESCE(f.IsCustomized, 0), COALESCE(f.IsDeleted, 0) FROM `_System_Field` f JOIN `_System_Object` o ON o.Id = f.ObjectId WHERE o.ApiName = ? AND f.ApiName = ? LIMIT 1",
		objectAPIName,
		fieldAPIName,
	).Scan(&state.id, &state.ownerKind, &state.ownerPack, &isCustomized, &isDeleted)
	if err == sql.ErrNoRows {
		return adminFieldAdmissionState{}, false, nil
	}
	if err != nil {
		return adminFieldAdmissionState{}, false, apperrors.NewInternalError(fmt.Sprintf("failed to load field owner tuple for %s", formatFieldAdmissionKey(objectAPIName, fieldAPIName)), err)
	}
	normalizeAdminFieldAdmissionState(&state)
	state.isCustomized = isCustomized != 0
	state.isDeleted = isDeleted != 0
	return state, true, nil
}

func normalizeAdminFieldAdmissionState(state *adminFieldAdmissionState) {
	state.ownerKind = strings.TrimSpace(state.ownerKind)
	state.ownerPack = strings.TrimSpace(state.ownerPack)
}

func validateAdminFieldMetadataAdmissionState(objectAPIName, fieldAPIName string, state adminFieldAdmissionState) error {
	fieldKey := formatFieldAdmissionKey(objectAPIName, fieldAPIName)
	if err := validateLiveAdminFieldAdmissionTuple(fieldKey, state, "metadata write"); err != nil {
		return err
	}
	switch state.ownerKind {
	case "", constants.FieldOwnerKindAdmin, constants.FieldOwnerKindRuntime:
		return nil
	case constants.FieldOwnerKindPack, constants.FieldOwnerKindPackRetained:
		return nil
	case constants.FieldOwnerKindPackRemoving:
		return apperrors.NewValidationError("OwnerKind", fmt.Sprintf(
			"field %s is being removed by source-backed pack %q and cannot be mutated through admin field metadata write",
			fieldKey,
			state.ownerPack,
		))
	default:
		return apperrors.NewValidationError("OwnerKind", fmt.Sprintf("field %s has unknown OwnerKind=%q; refusing admin field metadata write", fieldKey, state.ownerKind))
	}
}

func validateAdminFieldPhysicalAdmissionState(objectAPIName, fieldAPIName string, state adminFieldAdmissionState) error {
	fieldKey := formatFieldAdmissionKey(objectAPIName, fieldAPIName)
	if err := validateLiveAdminFieldAdmissionTuple(fieldKey, state, "physical write"); err != nil {
		return err
	}
	switch state.ownerKind {
	case "", constants.FieldOwnerKindAdmin, constants.FieldOwnerKindRuntime:
		return nil
	case constants.FieldOwnerKindPack, constants.FieldOwnerKindPackRetained, constants.FieldOwnerKindPackRemoving:
		return apperrors.NewValidationError("OwnerKind", fmt.Sprintf(
			"field %s is source-backed (OwnerKind=%q OwnerPack=%q) and cannot be mutated through admin field physical write",
			fieldKey,
			state.ownerKind,
			state.ownerPack,
		))
	default:
		return apperrors.NewValidationError("OwnerKind", fmt.Sprintf("field %s has unknown OwnerKind=%q; refusing admin field physical write", fieldKey, state.ownerKind))
	}
}

func validateLiveAdminFieldAdmissionTuple(fieldKey string, state adminFieldAdmissionState, action string) error {
	if state.isDeleted {
		return apperrors.NewValidationError("IsDeleted", fmt.Sprintf("field %s has a stale deleted owner tuple; refusing admin field %s", fieldKey, action))
	}
	switch state.ownerKind {
	case "", constants.FieldOwnerKindAdmin, constants.FieldOwnerKindRuntime:
		if state.ownerPack != "" {
			return malformedFieldOwnerTupleError(fieldKey, state, action)
		}
		return nil
	case constants.FieldOwnerKindPack, constants.FieldOwnerKindPackRetained, constants.FieldOwnerKindPackRemoving:
		if state.ownerPack == "" {
			return malformedFieldOwnerTupleError(fieldKey, state, action)
		}
		return nil
	default:
		return nil
	}
}

func malformedFieldOwnerTupleError(fieldKey string, state adminFieldAdmissionState, action string) error {
	return apperrors.NewValidationError("OwnerKind", fmt.Sprintf(
		"malformed field owner tuple for %s (OwnerKind=%q OwnerPack=%q IsCustomized=%v); refusing admin field %s",
		fieldKey,
		state.ownerKind,
		state.ownerPack,
		state.isCustomized,
		action,
	))
}

func formatFieldAdmissionKey(objectAPIName, fieldAPIName string) string {
	objectAPIName = strings.TrimSpace(objectAPIName)
	fieldAPIName = strings.TrimSpace(fieldAPIName)
	if objectAPIName == "" {
		return fieldAPIName
	}
	if fieldAPIName == "" {
		return objectAPIName
	}
	return objectAPIName + "." + fieldAPIName
}
