import { ObjectOwner } from '@mysten/sui/client'

import { MoveCallWithObjectDetails } from './move-call-object-fetcher'
import { IPTBValidator } from './ptb-validator'

/**
 * NonSenderObjectValidator ensures objects are not owned by the transaction sender
 *
 * In Sui, this validator checks that objects are:
 * - Shared: globally accessible, mutations require consensus
 * - Immutable: can't change after creation
 * - Owned by another address (not the sender)
 *
 * This prevents the sender from using their own owned objects in the transaction
 */
export class NonSenderObjectValidator implements IPTBValidator {
    private sender: string

    constructor(sender: string) {
        this.sender = sender
    }

    /**
     * Validates that objects are not owned by the sender
     * @param moveCallsWithDetails - Array of move calls with object details
     * @throws Error if any object is owned by the sender
     */
    validate(moveCallsWithDetails: MoveCallWithObjectDetails[]): void {
        for (const { objectDetails } of moveCallsWithDetails) {
            for (const [objectId, objectResponse] of objectDetails) {
                const owner = objectResponse.data?.owner
                if (owner == null) {
                    throw new Error(`Object ${objectId} has no owner`)
                }
                // Validate that object is not owned by the sender
                if (!this.isValidOwnership(owner)) {
                    const ownerType = this.getOwnershipType(owner)
                    throw new Error(
                        `Object ${objectId} is owned by the sender (${this.sender}). ` +
                            `Only Shared, Immutable, or objects owned by other addresses are allowed. ` +
                            `Current ownership: ${ownerType}`
                    )
                }
            }
        }
    }

    /**
     * Checks if the ownership is valid (not owned by sender)
     * @param owner - The object owner to check
     * @returns true if ownership is valid (Shared, Immutable, or owned by another address)
     */
    private isValidOwnership(owner: ObjectOwner): boolean {
        // Shared objects are valid
        if (typeof owner === 'object' && 'Shared' in owner) {
            return true
        }

        // Immutable objects are valid
        if (owner === 'Immutable') {
            return true
        }

        // Objects owned by another address are valid
        if (typeof owner === 'object' && 'AddressOwner' in owner && owner.AddressOwner !== this.sender) {
            return true
        }

        // Objects owned by another object are valid
        if (typeof owner === 'object' && 'ObjectOwner' in owner) {
            return true
        }

        // If owned by sender, it's invalid
        return false
    }

    /**
     * Gets a human-readable description of the ownership type
     * @param owner - The object owner
     * @returns String description of ownership type
     */
    private getOwnershipType(owner: ObjectOwner): string {
        if (typeof owner === 'object' && 'Shared' in owner) {
            return `Shared (initial_shared_version: ${owner.Shared.initial_shared_version})`
        }

        if (owner === 'Immutable') {
            return 'Immutable'
        }

        if (typeof owner === 'object' && 'AddressOwner' in owner) {
            return `AddressOwner: ${owner.AddressOwner}`
        }

        if (typeof owner === 'object' && 'ObjectOwner' in owner) {
            return `ObjectOwner: ${owner.ObjectOwner}`
        }

        return 'Unknown ownership type'
    }
}
