package reorg

import (
	"context"
	"fmt"
	"log"
	"math/big"

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

// ReorgHandler handles blockchain reorganizations
type ReorgHandler struct {
	db      *pgxpool.Pool
	client  *ethclient.Client
	chainID int
}

// NewReorgHandler creates a new reorg handler
func NewReorgHandler(db *pgxpool.Pool, client *ethclient.Client, chainID int) *ReorgHandler {
	return &ReorgHandler{
		db:      db,
		client:  client,
		chainID: chainID,
	}
}

// DetectReorg detects if a reorg has occurred
func (rh *ReorgHandler) DetectReorg(ctx context.Context, blockNumber int64) (bool, int64, error) {
	// Get stored block hash
	var storedHash string
	query := `SELECT hash FROM blocks WHERE chain_id = $1 AND number = $2`
	err := rh.db.QueryRow(ctx, query, rh.chainID, blockNumber).Scan(&storedHash)
	if err != nil {
		return false, 0, err
	}

	// Get current block hash from chain
	block, err := rh.client.BlockByNumber(ctx, big.NewInt(blockNumber))
	if err != nil {
		return false, 0, err
	}

	currentHash := block.Hash().Hex()

	// Compare hashes
	if storedHash != currentHash {
		// Reorg detected, find common ancestor
		commonAncestor, err := rh.findCommonAncestor(ctx, blockNumber)
		if err != nil {
			return false, 0, err
		}
		return true, commonAncestor, nil
	}

	return false, 0, nil
}

// findCommonAncestor finds the common ancestor block
func (rh *ReorgHandler) findCommonAncestor(ctx context.Context, startBlock int64) (int64, error) {
	// Binary search to find common ancestor
	low := int64(0)
	high := startBlock

	for low <= high {
		mid := (low + high) / 2

		var storedHash string
		err := rh.db.QueryRow(ctx,
			`SELECT hash FROM blocks WHERE chain_id = $1 AND number = $2`,
			rh.chainID, mid,
		).Scan(&storedHash)

		if err != nil {
			high = mid - 1
			continue
		}

		block, err := rh.client.BlockByNumber(ctx, big.NewInt(mid))
		if err != nil {
			high = mid - 1
			continue
		}

		if storedHash == block.Hash().Hex() {
			low = mid + 1
		} else {
			high = mid - 1
		}
	}

	return high, nil
}

// HandleReorg handles a detected reorg
func (rh *ReorgHandler) HandleReorg(ctx context.Context, commonAncestor int64) error {
	log.Printf("Handling reorg: common ancestor at block %d", commonAncestor)

	tx, err := rh.db.Begin(ctx)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer tx.Rollback(ctx)

	// Mark blocks as orphaned
	_, err = tx.Exec(ctx, `
		UPDATE blocks 
		SET orphaned = true, orphaned_at = NOW()
		WHERE chain_id = $1 AND number > $2 AND orphaned = false
	`, rh.chainID, commonAncestor)
	if err != nil {
		return fmt.Errorf("failed to mark blocks as orphaned: %w", err)
	}

	// Delete orphaned data (cascade will handle related records)
	_, err = tx.Exec(ctx, `
		DELETE FROM blocks 
		WHERE chain_id = $1 AND number > $2 AND orphaned = true
	`, rh.chainID, commonAncestor)
	if err != nil {
		return fmt.Errorf("failed to delete orphaned blocks: %w", err)
	}

	return tx.Commit(ctx)
}

