import { useQueryClient } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router";
import { toast } from "react-toastify";
import { formatEther, parseEther, type Address } from "viem";
import { useBalance, useConnection, useReadContract, useReadContracts, useWaitForTransactionReceipt, useWatchContractEvent, useWriteContract } from "wagmi";
import { LoadingView } from "~/components/loading/LoadingView";
import { auctionABI, auctionContractAddress } from "~/config/auction";


function WalletBalanceCard({ address }: { address: Address }) {
    const { data: balance, error } = useBalance({ address });

    if (error) {
        return <p>Failed to fetch wallet balance.</p>;
    }

    return (
        <div className="bg-white border border-slate-200 rounded-xl p-6 shadow-sm hover:shadow-md transition-shadow">
            <div className="flex justify-between items-start mb-4">
                <h3 className="font-bold text-lg">
                    Wallet Balance
                </h3>
                <span className="text-xs px-2 py-1 rounded-full font-semibold bg-slate-100 text-slate-600">Funds</span>
            </div>
            <div className="flex items-baseline gap-2">
                <span className="text-3xl font-bold text-slate-900">{balance ? Number(formatEther(balance.value)).toLocaleString() : '...'}</span>
                <span className="text-slate-500 font-medium">ETH</span>
            </div>
        </div>
    )
}

function BestOfferCard({ address }: { address: Address }) {
    const { data: bestOffer, error } = useReadContracts({
        contracts: [
            {
                address: auctionContractAddress,
                abi: auctionABI,
                functionName: 'highestAmount'
            },
            {
                address: auctionContractAddress,
                abi: auctionABI,
                functionName: 'highestBidder'
            }
        ]
    });

    const [amount, bidder] = bestOffer || [];

    if (error || amount?.error || bidder?.error) {
        return <p>Failed to fetch best bid.</p>;
    }

    return (
        <div className="bg-white border border-slate-200 rounded-xl p-6 shadow-sm hover:shadow-md transition-shadow">
            <div className="flex justify-between items-start mb-4">
                <h3 className="font-bold text-lg">
                    Best Offer
                </h3>
                <span className="text-xs px-2 py-1 rounded-full font-semibold bg-slate-100 text-slate-600">Top Bid</span>
            </div>
            <div className="flex items-baseline gap-2">
                <span className="text-3xl font-bold text-slate-900">{amount?.result !== undefined ? Number(formatEther(amount.result)).toLocaleString() : '...'}</span>
                <span className="text-slate-500 font-medium">ETH ({bidder?.result === address ? "by you" : "by someone else"})</span>
            </div>
        </div>
    )
}

function LastEventCard({ event }: { event?: string }) {
    return (
        <div className="bg-white border border-slate-200 rounded-xl p-6 shadow-sm hover:shadow-md transition-shadow">
            <div className="flex justify-between items-start mb-4">
                <h3 className="font-bold text-lg">
                    Last Event
                </h3>
                <span className="text-xs px-2 py-1 rounded-full font-semibold bg-slate-100 text-slate-600">Update</span>
            </div>
            <div className="flex items-baseline gap-2">
                <span className="text-xl text-slate-900 pt-2">{event || 'No events yet...'}</span>
            </div>
        </div>
    )
}

function PendingReturnCard({ address }: { address: Address }) {
    const { data: refundValue, error } = useReadContract({
        address: auctionContractAddress,
        abi: auctionABI,
        functionName: 'pendingReturnAmount',
        args: [address]
    });

    if (error) {
        return <p>Failed to fetch refund amount.</p>;
    }

    return (
        <div className="bg-white border border-slate-200 rounded-xl p-6 shadow-sm hover:shadow-md transition-shadow">
            <div className="flex justify-between items-start mb-4">
                <h3 className="font-bold text-lg">
                    Pending Return
                </h3>
                <span className="text-xs px-2 py-1 rounded-full font-semibold bg-slate-100 text-slate-600">Your Refund</span>
            </div>
            <div className="flex items-baseline gap-2">
                <span className="text-3xl font-bold text-slate-900">
                    {refundValue !== undefined ? Number(formatEther(refundValue)).toLocaleString() : '...'}
                </span>
                <span className="text-slate-500 font-medium">ETH</span>
            </div>
        </div>
    )
}


function MakeBidCard() {
    const [inputValue, setInputValue] = useState("");
    const isValidInput = inputValue && !isNaN(Number(inputValue)) && Number(inputValue) > 0;

    const { data: hash, mutate: makeBid, isPending: isWaitingForUser, error: userError, reset } = useWriteContract();
    const { data: receipt, isPending, isFetching, error: receiptError } = useWaitForTransactionReceipt({
        hash
    })

    function onSubmit(e) {
        e.preventDefault();

        if (!isValidInput) {
            toast.error('Invalid bid amount.');
            return;
        }

        makeBid({
            address: auctionContractAddress,
            abi: auctionABI,
            functionName: 'bid',
            value: parseEther(inputValue)
        })
    }

    return (
        <div className="bg-white border border-slate-200 rounded-xl p-6 shadow-sm hover:shadow-md transition-shadow lg:col-span-2 flex flex-col gap-4">
            <div className="flex justify-between items-start mb-4">
                <h3 className="font-bold text-lg">
                    Make Bid
                </h3>
                <span className="text-xs px-2 py-1 rounded-full font-semibold bg-slate-100 text-slate-600">Bid</span>
            </div>
            <div className="flex flex-col lg:flex-row lg:justify-between gap-4">
                <div className="flex flex-col w-full items-center justify-center gap-5 py-5">
                    <img alt="auction" className="w-14 h-14" src="https://img.icons8.com/ios-filled/50/2563eb/auction.png" />
                    <p className="text-center text-slate-500">
                        Enter your bid amount in ETH
                    </p>
                </div>
                <div className="w-full">
                    <form className="flex flex-col w-full gap-3 py-5" onSubmit={onSubmit}>
                        <input className="border border-gray-300 p-2 rounded-lg w-full" placeholder="Enter bid amount." type="text" value={inputValue}
                            onChange={(e) => {
                                setInputValue(e.target.value);
                                if (hash || userError) {
                                    reset();
                                }
                            }}
                        />
                        <button disabled={!isValidInput || isWaitingForUser || (isPending && isFetching)} type="submit" className="bg-blue-600 text-white px-5 py-2 cursor-pointer rounded-xl disabled:opacity-50 min-w-32">
                            Make Bid
                        </button>
                    </form>

                    {isWaitingForUser && <p>Waiting for user...</p>}
                    {(userError || receiptError) && <p>Failed to make bid.</p>}
                    {(isPending && isFetching) && <p>Waiting for receipt...</p>}
                    {receipt && <p>Status: {receipt.status}</p>}
                </div>
            </div>
        </div>
    )
}

export function WithdrawCard({ address }: { address: Address }) {

    const { data: refundValue, isPending: isRefundValuePending, error: refundValueError } = useReadContract({
        address: auctionContractAddress,
        abi: auctionABI,
        functionName: 'pendingReturnAmount',
        args: [address]
    });

    const queryClient = useQueryClient();
    const { data: hash, mutate: withdraw, isPending: isWaitingForUser, error: userError } = useWriteContract();
    const { data: receipt, isPending, isFetching, error: receiptError } = useWaitForTransactionReceipt({
        hash
    });

    useEffect(() => {
        if (hash && receipt?.status === 'success') {
            queryClient.invalidateQueries();
        }
    }, [receipt, hash, queryClient]);


    function onWithdraw(e) {
        e.preventDefault();

        withdraw({
            address: auctionContractAddress,
            abi: auctionABI,
            functionName: 'withdraw'
        })
    }

    if (refundValueError) {
        return <p>Failed to fetch refund value.</p>;
    }

    if (isRefundValuePending || !refundValue) {
        // don't render the component if there is nothing to withdraw
        return null;
    }

    return (
        <div className="bg-white border border-slate-200 rounded-xl p-6 shadow-sm hover:shadow-md transition-shadow lg:col-span-2 flex flex-col gap-4">
            <div className="flex justify-between items-start mb-4">
                <h3 className="font-bold text-lg">
                    Withdraw
                </h3>
                <span className="text-xs px-2 py-1 rounded-full font-semibold bg-slate-100 text-slate-600">Refund</span>
            </div>
            <div className="flex flex-col lg:flex-row lg:justify-between gap-4">
                <div className="flex flex-col w-full items-center justify-center gap-5 py-5">
                    <img alt="withdraw" className="w-14 h-14" src="https://img.icons8.com/ios-filled/50/2563eb/money.png" />
                    <p className="text-center text-slate-500">
                        Withdraw your funds
                    </p>
                </div>
                <div className="flex flex-col gap-5 w-full items-center justify-center">
                    <p className="text-slate-500 text-center">
                        Click the button to withdraw your funds
                    </p>
                    <button type="button" disabled={isWaitingForUser || (isPending && isFetching)} onClick={onWithdraw} 
                            className="bg-black text-white px-5 py-2 cursor-pointer rounded-xl disabled:opacity-50 mb-3 w-full">
                        Withdraw
                    </button>

                    {isWaitingForUser && <p>Waiting for user...</p>}
                    {(userError || receiptError) && <p>Failed to withdraw.</p>}
                    {(isFetching && isPending) && <p>Waiting for receipt...</p>}
                    {receipt && <p>Status: {receipt.status}</p>}
                </div>
            </div>
        </div>
    )
}


export function AuctionPage() {
    const { address, isConnected } = useConnection();
    const [isReady, setIsReady] = useState(false);
    const navigate = useNavigate();

    useEffect(() => {
        if (!isConnected) {
            navigate("/status");
        } else if (address && !isReady) {
            setIsReady(true);
        }
    }, [isConnected, address, isReady, navigate, setIsReady]);

    const [lastEvent, setLastEvent] = useState<string>();
    const queryClient = useQueryClient();
    useWatchContractEvent({
        address: auctionContractAddress,
        abi: auctionABI,
        onLogs: (logs) => {
            if (logs.length > 0) {
                setLastEvent(logs[logs.length - 1].eventName);
                queryClient.invalidateQueries();
            }
        }
    })

    if (!isConnected || !address || !isReady) {
        return <LoadingView />;
    }

    return (
        <div className="grid grid-cols-1 lg:grid-cols-2 gap-y-10 gap-x-6">
            <WalletBalanceCard address={address} />
            <BestOfferCard address={address} />
            <LastEventCard event={lastEvent} />
            <PendingReturnCard address={address} />
            <MakeBidCard />
            <WithdrawCard address={address} />
        </div>
    )
}