package managedlifecycle

import (
	"context"
	"database/sql"
	"fmt"
	"reflect"
)

const managedLifecycleEvidenceTable = "_System_ManagedComponentOperationEvidence"

type managedLifecycleEvidenceColumnShape struct {
	dataType          string
	length            sql.NullInt64
	datetimePrecision sql.NullInt64
	nullable          string
	defaultValue      sql.NullString
}

func validateManagedLifecycleEvidenceTable(ctx context.Context, db *sql.DB) error {
	rows, err := db.QueryContext(ctx, `
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, DATETIME_PRECISION, IS_NULLABLE, COLUMN_DEFAULT
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
  AND TABLE_NAME = ?`, managedLifecycleEvidenceTable)
	if err != nil {
		return fmt.Errorf("inspect managed lifecycle evidence columns: %w", err)
	}
	columns := make(map[string]managedLifecycleEvidenceColumnShape, 11)
	for rows.Next() {
		var name, dataType, nullable string
		var length, datetimePrecision sql.NullInt64
		var defaultValue sql.NullString
		if err := rows.Scan(&name, &dataType, &length, &datetimePrecision, &nullable, &defaultValue); err != nil {
			rows.Close()
			return fmt.Errorf("scan managed lifecycle evidence column: %w", err)
		}
		columns[name] = managedLifecycleEvidenceColumnShape{dataType: dataType, length: length, datetimePrecision: datetimePrecision, nullable: nullable, defaultValue: defaultValue}
	}
	if err := rows.Err(); err != nil {
		rows.Close()
		return fmt.Errorf("iterate managed lifecycle evidence columns: %w", err)
	}
	if err := rows.Close(); err != nil {
		return fmt.Errorf("close managed lifecycle evidence columns: %w", err)
	}
	length := func(value int64) sql.NullInt64 { return sql.NullInt64{Int64: value, Valid: true} }
	defaultValue := func(value string) sql.NullString { return sql.NullString{String: value, Valid: true} }
	want := map[string]managedLifecycleEvidenceColumnShape{
		"OperationId":            {dataType: "varbinary", length: length(64), nullable: "NO"},
		"Attempt":                {dataType: "int", nullable: "NO"},
		"ComponentKind":          {dataType: "varchar", length: length(255), nullable: "NO"},
		"Scope":                  {dataType: "varchar", length: length(16), nullable: "NO"},
		"PlanFingerprint":        {dataType: "binary", length: length(32), nullable: "NO"},
		"ProjectionIdentityHash": {dataType: "binary", length: length(32), nullable: "NO"},
		"MutationDisposition":    {dataType: "varchar", length: length(32), nullable: "NO"},
		"SemanticMutationRows":   {dataType: "int", nullable: "NO"},
		"EffectReceiptHash":      {dataType: "binary", length: length(32), nullable: "YES"},
		"CreatedDate":            {dataType: "datetime", datetimePrecision: length(6), nullable: "NO", defaultValue: defaultValue("CURRENT_TIMESTAMP(6)")},
		"LastModifiedDate":       {dataType: "datetime", datetimePrecision: length(6), nullable: "NO", defaultValue: defaultValue("CURRENT_TIMESTAMP(6)")},
	}
	if !reflect.DeepEqual(columns, want) {
		return fmt.Errorf("managed lifecycle evidence table column shape = %#v, want %#v", columns, want)
	}
	indexRows, err := db.QueryContext(ctx, `
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE CONSTRAINT_SCHEMA = DATABASE()
  AND TABLE_NAME = ?
  AND CONSTRAINT_NAME = 'PRIMARY'
ORDER BY ORDINAL_POSITION`, managedLifecycleEvidenceTable)
	if err != nil {
		return fmt.Errorf("inspect managed lifecycle evidence primary key: %w", err)
	}
	var primaryKey []string
	for indexRows.Next() {
		var column string
		if err := indexRows.Scan(&column); err != nil {
			indexRows.Close()
			return fmt.Errorf("scan managed lifecycle evidence primary key: %w", err)
		}
		primaryKey = append(primaryKey, column)
	}
	if err := indexRows.Err(); err != nil {
		indexRows.Close()
		return fmt.Errorf("iterate managed lifecycle evidence primary key: %w", err)
	}
	if err := indexRows.Close(); err != nil {
		return fmt.Errorf("close managed lifecycle evidence primary key: %w", err)
	}
	if !reflect.DeepEqual(primaryKey, []string{"OperationId", "Attempt"}) {
		return fmt.Errorf("managed lifecycle evidence primary key = %q, want [OperationId Attempt]", primaryKey)
	}
	return nil
}
