package verification

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"path/filepath"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/jackc/pgx/v5/pgxpool"
)

// Verifier handles contract verification
type Verifier struct {
	db     *pgxpool.Pool
	client *ethclient.Client
	chainID int
}

// NewVerifier creates a new contract verifier
func NewVerifier(db *pgxpool.Pool, client *ethclient.Client, chainID int) *Verifier {
	return &Verifier{
		db:      db,
		client:  client,
		chainID: chainID,
	}
}

// VerifyRequest represents a verification request
type VerifyRequest struct {
	Address            string `json:"address"`
	CompilerVersion    string `json:"compiler_version"`
	OptimizationEnabled bool  `json:"optimization_enabled"`
	OptimizationRuns  int    `json:"optimization_runs"`
	EVMVersion        string `json:"evm_version"`
	SourceCode        string `json:"source_code"`
	ConstructorArgs   string `json:"constructor_arguments"`
	VerificationMethod string `json:"verification_method"`
}

// Verify verifies a contract
func (v *Verifier) Verify(ctx context.Context, req *VerifyRequest) (*VerificationResult, error) {
	// Get deployed bytecode
	deployedBytecode, err := v.client.CodeAt(ctx, common.HexToAddress(req.Address), nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get deployed bytecode: %w", err)
	}

	// Compile source code
	compiledBytecode, err := v.compileSource(req)
	if err != nil {
		return nil, fmt.Errorf("failed to compile source: %w", err)
	}

	// Compare bytecodes
	matches := v.compareBytecode(deployedBytecode, compiledBytecode)

	result := &VerificationResult{
		Address:         req.Address,
		Status:          "failed",
		CompilerVersion: req.CompilerVersion,
	}

	if matches {
		result.Status = "verified"
		// Store verification in database
		if err := v.storeVerification(ctx, req, result); err != nil {
			return nil, fmt.Errorf("failed to store verification: %w", err)
		}
	}

	return result, nil
}

// compileSource compiles Solidity source code
func (v *Verifier) compileSource(req *VerifyRequest) ([]byte, error) {
	// Create temporary directory
	_ = filepath.Join("/tmp", "verification", hex.EncodeToString(sha256.New().Sum([]byte(req.Address)))[:16])
	// Implementation would:
	// 1. Create standard JSON input
	// 2. Run solc compiler
	// 3. Extract bytecode
	// Simplified for now
	return nil, fmt.Errorf("compilation not implemented")
}

// compareBytecode compares deployed bytecode with compiled bytecode
func (v *Verifier) compareBytecode(deployed, compiled []byte) bool {
	// Remove metadata hash (last 53 bytes) from deployed
	if len(deployed) < 53 {
		return false
	}
	deployed = deployed[:len(deployed)-53]

	// Compare
	if len(deployed) != len(compiled) {
		return false
	}

	for i := range deployed {
		if deployed[i] != compiled[i] {
			return false
		}
	}

	return true
}

// storeVerification stores verification result in database
func (v *Verifier) storeVerification(ctx context.Context, req *VerifyRequest, result *VerificationResult) error {
	query := `
		INSERT INTO contracts (
			chain_id, address, name, compiler_version, optimization_enabled,
			optimization_runs, evm_version, source_code, abi, constructor_arguments,
			verification_status, verification_method, verified_at
		) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, NOW())
		ON CONFLICT (chain_id, address) DO UPDATE SET
			verification_status = $11,
			verified_at = NOW(),
			updated_at = NOW()
	`

	// Parse ABI from compilation result (simplified)
	var abi json.RawMessage

	_, err := v.db.Exec(ctx, query,
		v.chainID,
		req.Address,
		nil, // name
		req.CompilerVersion,
		req.OptimizationEnabled,
		req.OptimizationRuns,
		req.EVMVersion,
		req.SourceCode,
		abi,
		req.ConstructorArgs,
		result.Status,
		req.VerificationMethod,
	)

	return err
}

// VerificationResult represents verification result
type VerificationResult struct {
	Address         string `json:"address"`
	Status          string `json:"status"`
	CompilerVersion string `json:"compiler_version"`
	Error           string `json:"error,omitempty"`
}

