import { Card, CardDescription, CardTitle } from "@/components/ui/card"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Button } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
import {
    Select,
    SelectContent,
    SelectGroup,
    SelectItem,
    SelectLabel,
    SelectTrigger,
    SelectValue,
} from "@/components/ui/select"
import { ArrowRight, Trash2, KeyRound } from "lucide-react"
import { useEffect, useState } from "react"
import { Layout2 } from "@/components/Layout2"
import { Link } from "react-router-dom"
import useSWR, { mutate } from "swr"
import { rpc } from "@/containers/WalletPopup/api"
import { ss58Address } from "@polkadot-labs/hdkd-helpers"
import {
    CreateDeriveFn,
    ecdsaCreateDerive,
    ed25519CreateDerive,
    sr25519CreateDerive,
} from "@polkadot-labs/hdkd"
import React from "react"
import { Header, BottomNavBar } from "../../components"
import { networks } from "@/constants"

type Scheme = "Sr25519" | "Ed25519" | "Ecdsa"

const createDeriveFnMap: Record<Scheme, CreateDeriveFn> = {
    Sr25519: sr25519CreateDerive,
    Ed25519: ed25519CreateDerive,
    Ecdsa: ecdsaCreateDerive,
}

type AccountCardProps = React.ComponentProps<typeof Card> & {
    name: string
    ss58Address: string
    index: number
    meta: string | undefined
}

const AccountCard: React.FC<AccountCardProps> = (props) => {
    if (!props.meta) return null
    return (
        <Card data-testid={props.name} key={props.name} className="p-2 border-none rounded bg-background">
            <div className="flex items-center justify-between">
                <div>
                    <CardTitle className="font-semibold text-base mb-0.5">
                        {props.name ? props.name : "Root Address" }
                    </CardTitle>
                    <CardDescription className="text-xs truncate max-w-[240px]">
                        {props.ss58Address}
                    </CardDescription>
                </div>
                <Button variant="ghost" asChild>
                    <Link
                        data-testid={`${props.name}-expand`}
                        to={`/accounts/${props.ss58Address}?meta=${props.meta}&index=${props.index}`}
                    >
                        <ArrowRight className="w-4 h-4" />
                    </Link>
                </Button>
            </div>
        </Card>
    )
}

const AccountsSkeleton: React.FC = () => {
    return new Array(10).fill(null).map((_, i) => (
        <Card
            key={`account-skeleton-card-${i}`}
            className="p-2 mb-2 transition duration-300 border-none rounded bg-background"
        >
            <div className="flex items-center justify-between">
                <div className="w-full">
                    <Skeleton className="w-5/12 h-4 mb-2" />
                    <Skeleton className="w-11/12 h-3" />
                </div>
                <Button variant="ghost" disabled>
                    <Skeleton className="w-4 h-4" />
                </Button>
            </div>
        </Card>
    ))
}

export const Accounts = () => {
    const { data: cryptoKeys, isLoading: isFetchingCryptoKeys } = useSWR(
        "rpc.getCryptoKeys",
        () => rpc.client.getCryptoKeys(),
        {
            revalidateOnFocus: true,
        },
    )

    const [selectedCryptoKeyName, setSelectedCryptoKeyName] = useState<string | undefined>()
    const [isPending, setIsPending] = useState<boolean>(false)
    const [error, setError] = useState<string>()

    useEffect(() => {
        const storedCryptoKey = window.localStorage.getItem("selectedCryptoKeyName")
        if (!storedCryptoKey) return

        setSelectedCryptoKeyName(storedCryptoKey)
    }, [])

    useEffect(() => {
        if (!selectedCryptoKeyName) window.localStorage.removeItem("selectedCryptoKeyName")
        else window.localStorage.setItem("selectedCryptoKeyName", selectedCryptoKeyName)
    }, [selectedCryptoKeyName])

    const keygroup = cryptoKeys?.find((k) => k.name === selectedCryptoKeyName) ?? cryptoKeys?.[0]
    const accounts = keygroup?.accounts ?? []

    const getChainPrefix = (chainId: string) => {
        const foundPrefix = networks.find(network => network.chainId === chainId)?.prefix
        return foundPrefix !== undefined ? foundPrefix : 1996
    }

    const pendingWrapper = (func: any) => {
        return async (...args: any[]) => {
            try {
                setError("")
                setIsPending(true)
                await func(...args)
                await mutate("rpc.getCryptoKeys")
            } catch (e) {
                if (e instanceof Error) setError(e.message)
            } finally {
                setIsPending(false)
            }
        }
    }

    const deriveCryptoAccount = async (derivationPath: string) => {
        let number = 0
        const match = derivationPath.match(/\d+$/)
        if (match) {
            number = parseInt(match[0], 10) + 1
        }
        await rpc.client.updateCryptoKey({
            name: selectedCryptoKeyName ? selectedCryptoKeyName : "",
            deviation: number,
        })
    }

    const clearCryptoKeys = async (name: string) => {
        await rpc.client.clearCryptoKeys({ name })
    }

    const wrappedDeriveCryptoAccount = pendingWrapper(deriveCryptoAccount)
    const wrappedClearCryptoKeys = pendingWrapper(clearCryptoKeys)

    const keysetAccounts = accounts
        .filter((account) => account.type === "Keyset")
        .map(({ path, publicKey, chainId }) => [path, ss58Address(publicKey, getChainPrefix(chainId))] as const)
    const keypairAccounts = accounts
        .filter((account) => account.type === "Keypair")
        .map(({ publicKey, chainId }) => ss58Address(publicKey, getChainPrefix(chainId)))

    return (
        <Layout2 innerClassName="bg-muted">
            <Header />
            <div className="flex items-center justify-between px-6 my-4 sm:px-8">
                <h2 className="text-xl font-semibold">Accounts</h2>
                <Select
                    disabled={!cryptoKeys || cryptoKeys.length === 0}
                    value={selectedCryptoKeyName}
                    onValueChange={(value) => setSelectedCryptoKeyName(value)}
                >
                    <SelectTrigger className="w-[235px]" data-testid="accounts-select">
                        <SelectValue placeholder="Select Crypto Key" />
                    </SelectTrigger>
                    <SelectContent>
                        <SelectGroup>
                            <SelectLabel>Crypto Keys</SelectLabel>
                            {(cryptoKeys ?? []).map((key) => (
                                <SelectItem
                                    data-testid={`accounts-select-${key.name}`}
                                    key={key.name}
                                    value={key.name}
                                >
                                    {`${key.name} <${key.scheme.toLowerCase()}>`}
                                </SelectItem>
                            ))}
                        </SelectGroup>
                    </SelectContent>
                </Select>
            </div>

            <ScrollArea className="px-6 mb-4 grow sm:px-8">
                <section>
                    {isFetchingCryptoKeys && <AccountsSkeleton />}
                    {!isFetchingCryptoKeys && (
                        <div className="space-y-2">
                            {keysetAccounts.map(([derivationPath, ss58Address], i) => (
                                <AccountCard
                                    index={i}
                                    meta={selectedCryptoKeyName}
                                    name={derivationPath}
                                    ss58Address={ss58Address}
                                />
                            ))}
                            {keypairAccounts.map((ss58Address, i) => (
                                <AccountCard
                                    index={i}
                                    meta={selectedCryptoKeyName}
                                    name={`Account ${i}`}
                                    ss58Address={ss58Address}
                                />
                            ))}
                            {error && (
                                <div className="text-sm font-medium text-destructive">
                                    {error}
                                </div>
                            )}
                        </div>
                    )}
                </section>
                {!isFetchingCryptoKeys && (
                    <div className="pt-5 flex flex-row justify-between gap-2">
                        {keysetAccounts.length > 0 && (
                            <Button
                                disabled={isPending || isFetchingCryptoKeys}
                                variant="secondary"
                                size="full"
                                className="flex items-center"
                                onClick={() => wrappedDeriveCryptoAccount(keysetAccounts[keysetAccounts.length - 1][0])}
                            >
                                <KeyRound className="w-5 h-5 mr-2" />
                                Create Address
                            </Button>)
                        }
                        {(keysetAccounts.length > 0 || keypairAccounts.length > 0) && (
                            <Button
                                disabled={isPending || isFetchingCryptoKeys}
                                variant="secondary"
                                size="full"
                                className="flex items-center"
                                onClick={() => wrappedClearCryptoKeys(selectedCryptoKeyName).then((_) => {
                                    setSelectedCryptoKeyName(cryptoKeys?.at(-2)?.name)
                                })}
                            >
                                <Trash2 className="w-5 h-5 mr-2" />
                                Delete Account
                            </Button>
                        )}
                    </div>
                )}
            </ScrollArea>

            <BottomNavBar currentItem="home" />
        </Layout2>
    )
}
