import { Key, NotepadText, ChevronDown, Sprout } from "lucide-react"
import { rpc } from "../../api"
import { z } from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { SubmitHandler, useForm } from "react-hook-form"
import { useNavigate } from "react-router-dom"
import { useState } from "react"
import {
    entropyToMiniSecret,
    sr25519,
    ed25519,
    ecdsa,
    mnemonicToEntropy
} from "@polkadot-labs/hdkd-helpers"
import { fromHex, toHex } from "@polkadot-api/utils"
import { bytesToHex } from "@noble/ciphers/utils"
import {
    CreateDeriveFn,
    ecdsaCreateDerive,
    ed25519CreateDerive,
    sr25519CreateDerive,
} from "@polkadot-labs/hdkd"
import { networks } from "@/constants"
import { Layout2 } from "@/components/Layout2"
import { BottomNavBar, Header } from "../../components"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Checkbox } from "@/components/ui/checkbox"

import {
    Card,
    CardContent,
    CardHeader,
    CardTitle,
    CardDescription,
    CardFooter,
} from "@/components/ui/card"
import {
    Form,
    FormField,
    FormItem,
    FormLabel,
    FormControl,
    FormMessage,
} from "@/components/ui/form"
import {
    Select,
    SelectValue,
    SelectTrigger,
    SelectLabel,
    SelectContent,
    SelectGroup,
    SelectItem,
} from "@/components/ui/select"

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

const privateKeyIsValid = (value: string | undefined, scheme: string) => {
    if (!value) return false

    const bytes = fromHex(value)
    try {
        switch (scheme) {
            case "Ecdsa":
                ecdsa.getPublicKey(bytes)
                break
            case "Ed25519":
                ed25519.getPublicKey(bytes)
                break
            case "Sr25519":
                sr25519.getPublicKey(value)
                break
            default:
                return false
        }
        return true
    } catch (_) {
        return false
    }
}

const mnemonicIsValid = (value: string | undefined) => {
    if (!value) return false

    try {
        mnemonicToEntropy(value)
        return true
    } catch (_) {
        return false
    }
}

const importAccountScheme = z
    .object({
        tab: z.union([
            z.object({
                _type: z.enum(["mnemonic", "privateKey", "seed"]),
                mnemonic: z.string(),
            }),
            z.object({
                _type: z.enum(["mnemonic", "privateKey", "seed"]),
                privateKey: z.string(),
            }),
            z.object({
                _type: z.enum(["mnemonic", "privateKey", "seed"]),
                seed: z.string(),
            })
        ]),
        scheme: z.string(),
        keyname: z.string().min(1, "Key name cannot be empty"),
    })

type FormFields = z.infer<typeof importAccountScheme>

export function ImportAccounts() {
    const navigate = useNavigate()

    const form = useForm<FormFields>({
        resolver: zodResolver(importAccountScheme),
        defaultValues: {
            tab: {
                _type: "mnemonic",
                mnemonic: "",
            },
            scheme: "",
            keyname: "",
        }
    })

    const {
        handleSubmit,
        watch,
        getValues,
        getFieldState,
        formState: { isSubmitting, isValid, isReady },
        control,
        setValue,
        setError,
        clearErrors,
    } = form

    const activeTab = watch("tab._type")

    const onActiveTabChanged = (tab: Tab["_type"]) => {
        clearErrors()
        switch (tab) {
            case "mnemonic":
                setValue("tab", { _type: "mnemonic", mnemonic: "" })
                break;
            case "privateKey":
                setValue("tab", { _type: "privateKey", privateKey: "" })
                break;
            case "seed":
                setValue("tab", { _type: "seed", seed: "" })
                break;
            default:
                setValue("tab", { _type: "privateKey", privateKey: "" })
                break;
        }
        setValue("scheme", "")
        setValue("keyname", "")
    }

    const onSubmit: SubmitHandler<FormFields> = async (data) => {
        let errorOccured = null

        const keynameExists = await rpc.client.getCryptoKey(data.keyname)
        if (keynameExists) {
            errorOccured = {
                field: "keyname",
                message: "Account with this name already exists",
            }
        } else {
            switch (data.tab._type) {
                case "privateKey": {
                    try {
                        if (!privateKeyIsValid(data.tab.privateKey, data.scheme)) {
                            throw new Error("a")
                        }
                        await rpc.client.insertCryptoKey({
                            type: "Keypair",
                            name: data.keyname,
                            scheme: data.scheme,
                            privatekey: data.tab.privateKey,
                            createdAt: Date.now(),
                        })
                    } catch {
                        errorOccured = {
                            field: "tab.privateKey",
                            message: "Could not insert private key at selected scheme",
                        }
                    }
                    break
                }
                case "seed": {
                    try {
                        const miniSecret = fromHex(data.tab.seed)
                        const derive = createDeriveFnMap[data.scheme](miniSecret)
                        const derivationPaths = networks
                            .map((network) => {
                                return {
                                    chainId: network.chainId,
                                    path: "",
                                    publicKey: toHex(derive("").publicKey),
                                }
                            })

                        await rpc.client.insertCryptoKey({
                            type: "Keyset",
                            name: data.keyname,
                            scheme: data.scheme,
                            miniSecret: data.tab.seed,
                            derivationPaths,
                            createdAt: Date.now(),
                        })
                    } catch {
                        errorOccured = {
                            field: "tab.seed",
                            message: "Incorrect secret seed at selected scheme",
                        }
                    }
                    break
                }
                case "mnemonic": {
                    try {
                        if (!mnemonicIsValid(data.tab.mnemonic)) {
                            throw new Error("a")
                        }

                        const entropy = mnemonicToEntropy(data.tab.mnemonic)
                        const miniSecret = entropyToMiniSecret(entropy)

                        const derive = createDeriveFnMap[data.scheme](miniSecret)
                        const derivationPaths = networks
                            .map((network) => {
                                const path = `//${network.value}/0`
                                return {
                                    chainId: network.chainId,
                                    path,
                                    publicKey: toHex(derive(`//${network.value}/0`).publicKey),
                                }
                            })

                        await rpc.client.insertCryptoKey({
                            type: "Keyset",
                            name: data.keyname,
                            scheme: data.scheme,
                            miniSecret: bytesToHex(miniSecret),
                            derivationPaths,
                            createdAt: Date.now(),
                        })
                    } catch {
                        errorOccured = {
                            field: "tab.mnemonic",
                            message: "Could not use provided mnemonic phrase",
                        }
                    }
                    break
                }
            }
        }
        if (errorOccured) {
            setError(errorOccured.field, {
                type: "server",
                message: errorOccured.message,
            })
        } else {
            window.localStorage.setItem("selectedCryptoKeyName", data.keyname)
            navigate("/accounts")
        }
    }

    return (
        <Layout2 innerClassName="bg-muted">
            <Header />
            <Form {...form}>
                <form className="relative grow" onSubmit={handleSubmit(onSubmit)}>
                    <Card className="bg-muted border-none h-full flex flex-col flex-grow w-full max-w-md shadow-none">
                        <CardHeader className="space-y-0 pt-4 gap-4">
                            <CardTitle className="text-xl font-semibold tracking-normal">
                                Import Crypto Key
                            </CardTitle>
                            <CardDescription className="text-accent flex justify-between">
                                <Button
                                    type="button"
                                    variant="ghost"
                                    disabled={isSubmitting}
                                    onClick={() => onActiveTabChanged("mnemonic")}
                                    className={`text-xs p-2 ${activeTab === "mnemonic" ? "font-semibold underline" : ""}`}
                                >
                                    <NotepadText className="inline-block mr-2" />
                                    Mnemonic
                                </Button>
                                <Button
                                    type="button"
                                    variant="ghost"
                                    disabled={isSubmitting}
                                    onClick={() => onActiveTabChanged("seed")}
                                    className={`text-xs p-2 ${activeTab === "seed" ? "font-semibold underline" : ""}`}
                                >
                                    <Sprout className="inline-block mr-2" />
                                    Secret Seed
                                </Button>
                                <Button
                                    type="button"
                                    variant="ghost"
                                    disabled={isSubmitting}
                                    onClick={() => onActiveTabChanged("privateKey")}
                                    className={`text-xs p-2 ${activeTab === "privateKey" ? "font-semibold underline" : ""}`}
                                >
                                    <Key className="inline-block mr-2" />
                                    Private Key
                                </Button>
                            </CardDescription>
                        </CardHeader>
                        <CardContent className="content-center flex flex-col flex-grow justify-start">
                            {activeTab === "privateKey" && (
                                <FormField
                                    control={control}
                                    name="tab.privateKey"
                                    render={({ field }) => (
                                        <FormItem className="mb-4">
                                            <FormControl>
                                                <Input
                                                    {...field}
                                                    disabled={isSubmitting}
                                                    aria-label="Enter private key"
                                                    type="text"
                                                    className="pr-10"
                                                    placeholder="Enter private key"
                                                />
                                            </FormControl>
                                            <FormMessage />
                                        </FormItem>
                                    )}
                                />)}
                            {activeTab === "mnemonic" && (
                                <FormField
                                    control={control}
                                    name="tab.mnemonic"
                                    render={({ field }) => (
                                        <FormItem className="mb-4">
                                            <FormControl>
                                                <Textarea
                                                    {...field}
                                                    disabled={isSubmitting}
                                                    aria-label="Enter mnemonic phrase"
                                                    id="text"
                                                    rows={4}
                                                    className="min-h-auto h-auto text-justify resize-none"
                                                    placeholder="Enter mnemonic phrase"
                                                />
                                            </FormControl>
                                            <FormMessage />
                                        </FormItem>
                                    )}
                                />)}
                            {activeTab === "seed" && (
                                <FormField
                                    control={control}
                                    name="tab.seed"
                                    render={({ field }) => (
                                        <FormItem className="mb-4">
                                            <FormControl>
                                                <Input
                                                    {...field}
                                                    disabled={isSubmitting}
                                                    aria-label="Enter secret seed"
                                                    type="text"
                                                    className="pr-10"
                                                    placeholder="Enter secret seed"
                                                />
                                            </FormControl>
                                            <FormMessage />
                                        </FormItem>
                                    )}
                                />)}
                            <FormField
                                control={control}
                                name="keyname"
                                render={({ field }) => (
                                    <FormItem className="mb-4">
                                        <FormControl>
                                            <Input
                                                {...field}
                                                disabled={isSubmitting}
                                                aria-label="Enter crypto key name"
                                                type="text"
                                                className="pr-10"
                                                placeholder="Enter crypto key name"
                                            />
                                        </FormControl>
                                        <FormMessage />
                                    </FormItem>
                                )}
                            />
                            <FormField
                                control={control}
                                name="scheme"
                                render={({ field }) => (
                                    <FormItem className="mb-4">
                                        <FormControl>
                                            <Select
                                                {...field}
                                                disabled={isSubmitting}
                                                onValueChange={(scheme) => field.onChange(scheme)}
                                            >
                                                <SelectTrigger
                                                    className={`${field.value ? "text-primary" : "text-muted-foreground"} w-full`}
                                                    data-testid="scheme-select"
                                                >
                                                    <SelectValue placeholder="Select Key Type" />
                                                </SelectTrigger>
                                                <SelectContent>
                                                    <SelectGroup>
                                                        <SelectLabel>Crypto scheme</SelectLabel>
                                                        <SelectItem data-testid="scheme-sr25519" value="Sr25519">Sr25519</SelectItem>
                                                        <SelectItem data-testid="scheme-ed25519" value="Ed25519">Ed25519</SelectItem>
                                                        <SelectItem data-testid="scheme-ecdsa" value="Ecdsa">ECDSA</SelectItem>
                                                    </SelectGroup>
                                                </SelectContent>
                                            </Select>
                                        </FormControl>
                                    </FormItem>
                                )}
                            />
                        </CardContent>
                        <CardFooter className="flex justify-between mt-4">
                            <Button
                                disabled={isSubmitting}
                                size="full"
                                variant="secondary"
                            >
                                Import Key
                            </Button>
                        </CardFooter>
                    </Card>
                </form>
            </Form>
            <BottomNavBar currentItem="import" />
        </Layout2>
    )
}
