package persistence

import (
	"context"
	"database/sql"
	"errors"
)

// ApprovalLockAuthority resolves the immutable editability policy for one
// ProcessDefinition identity. Runtime binds this authority from the exact
// request Catalog revision; legacy compositions continue to use their existing
// ProcessDefinition row until they are retired.
type ApprovalLockAuthority interface {
	ApprovalLockType(context.Context, string) (string, error)
}

type approvalLockAuthorityContextKey struct{}

// WithApprovalLockAuthority binds the request-owned Approval editability
// authority used by record permission checks inside the same transaction.
func WithApprovalLockAuthority(ctx context.Context, authority ApprovalLockAuthority) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}
	return context.WithValue(ctx, approvalLockAuthorityContextKey{}, authority)
}

func approvalLockAuthorityFromContext(ctx context.Context) (ApprovalLockAuthority, bool) {
	if ctx == nil {
		return nil, false
	}
	authority, ok := ctx.Value(approvalLockAuthorityContextKey{}).(ApprovalLockAuthority)
	return authority, ok && authority != nil
}

func (r *PermissionRepository) GetPendingApprovalLockInfo(ctx context.Context, targetObjectID string) (processInstanceID string, lockType string, err error) {
	if authority, ok := approvalLockAuthorityFromContext(ctx); ok {
		var processDefinitionID string
		err = r.getExecutor(ctx).QueryRowContext(ctx, `
			SELECT Id, ProcessDefinitionId
			FROM ProcessInstance
			WHERE TargetObjectId = ? AND Status = 'Pending'
			ORDER BY CreatedDate DESC
			LIMIT 1
		`, targetObjectID).Scan(&processInstanceID, &processDefinitionID)
		if errors.Is(err, sql.ErrNoRows) {
			return "", "", nil
		}
		if err != nil {
			return "", "", err
		}
		lockType, err = authority.ApprovalLockType(ctx, processDefinitionID)
		if err != nil {
			return "", "", err
		}
		return processInstanceID, lockType, nil
	}

	const query = `
		SELECT pi.Id, COALESCE(pd.LockType, '')
		FROM ProcessInstance pi
		INNER JOIN ProcessDefinition pd ON pd.Id = pi.ProcessDefinitionId
		WHERE pi.TargetObjectId = ? AND pi.Status = 'Pending'
		ORDER BY pi.CreatedDate DESC
		LIMIT 1
	`

	err = r.getExecutor(ctx).QueryRowContext(ctx, query, targetObjectID).Scan(&processInstanceID, &lockType)
	if err == sql.ErrNoRows {
		return "", "", nil
	}
	if err != nil {
		return "", "", err
	}
	return processInstanceID, lockType, nil
}

func (r *PermissionRepository) HasPendingApprovalWorkitemForUser(ctx context.Context, processInstanceID, userID string) (bool, error) {
	const query = `
		SELECT 1
		FROM ProcessInstanceWorkitem
		WHERE ProcessInstanceId = ? AND ActorId = ?
		LIMIT 1
	`

	var exists int
	err := r.getExecutor(ctx).QueryRowContext(ctx, query, processInstanceID, userID).Scan(&exists)
	if err == sql.ErrNoRows {
		return false, nil
	}
	if err != nil {
		return false, err
	}
	return true, nil
}
