import * as React from 'react';
import ButtonGroup from '@mui/material/ButtonGroup';
import Button from '@mui/material/Button';
import Avatar from '@mui/material/Avatar';
import {
  Grid,
  Card,
  CardContent,
  CardActions,
  Typography,
  Box,
  Chip,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  IconButton,
  Tooltip,
  CircularProgress,
} from "@mui/material";
import ServerIcon from '@mui/icons-material/Dns';
import SignalCellularAltIcon from '@mui/icons-material/SignalCellularAlt';
import AttachMoneyIcon from '@mui/icons-material/AttachMoney';
import StarIcon from '@mui/icons-material/Star';
import Rating from '@mui/material/Rating';
import RefreshIcon from '@mui/icons-material/Refresh';
import useAuth from '~/hooks/useAuth';
import type { AccountInfo } from '~/context/AuthProvider';
import { signMessage } from './Metamask/Connections';
import { generateWireguardKeyPair, downloadWireguardConfig } from './WireguardConfig';
import type { Node } from './Util';
import { getActiveNodes, getNextNonce, processPayment, abortConnection } from './Contracts/Connections';
import axios from 'axios';
import { useEffect } from 'react';
import { Web3 } from 'web3';



function NodeItem({node, auth}: {node: Node, auth: AccountInfo}) {
    const [ratingOpen, setRatingOpen] = React.useState(false);
    const [ratingValue, setRatingValue] = React.useState<number | null>(null);
    const [submittingRating, setSubmittingRating] = React.useState(false);
    let nodeSignature = "";
    const getUrl = () => `http://${node.ip}:${node.port}`;
    const connect = async () => {
        try {
            const { privatekey: clientPrivateKey, publicKey: clientPublicKey } = generateWireguardKeyPair();
            const nonce: BigInt = await getNextNonce(auth.providerWithInfo.provider, auth.accounts[0]);
            const connectionStartTime = Math.floor(Date.now() / 1000);
            console.log("Nonce:", nonce.toString());
            const sig = await signMessage(nonce + clientPublicKey + connectionStartTime + node.pricePerMinute,auth.providerWithInfo.provider, auth.accounts[0]);
            const res_string = nonce + '\n' + clientPublicKey + '\n' + connectionStartTime + '\n' + node.pricePerMinute + '\n' + sig;
            let response = await axios.post(getUrl() + "/connect", res_string);
            const clientCIDR = response.data.WireguardClientCIDR;
            const serverPublicKey = response.data.WireguardServerPublicKey;
            const peerPort = response.data.WireguardPort;
            const dns = response.data.WireguardDNS;
            console.log(response.data);
            if(response.data.NodeSignature){
                const provider = auth.providerWithInfo.provider;
                const web3 = new Web3(provider);
                const recoveredAddress = web3.eth.accounts.recover(nonce.toString() + connectionStartTime + node.pricePerMinute, response.data.NodeSignature);
                if(recoveredAddress === node.address){
                    nodeSignature = response.data.NodeSignature;
                }
            }
            if(nodeSignature === ""){
                await abortConnection(auth.providerWithInfo.provider, auth.accounts[0],
                    clientPublicKey,
                    BigInt(connectionStartTime),
                    node.pricePerMinute,
                    String(sig)
                );
                return;
            }
            localStorage.setItem(node.address, JSON.stringify({
                vpnClientPublicKey: clientPublicKey,
                connectionStartTime: connectionStartTime,
                agreedPricePerMinute: node.pricePerMinute.toString(),
                clientSignature: String(sig),
                nodeSignature: nodeSignature
            }));
            downloadWireguardConfig(clientPrivateKey, serverPublicKey, clientCIDR, dns, node.ip, String(peerPort), "0.0.0.0/0");
        } catch (error) {
            console.error('Error:', error);
        }
    }

    const sendPayment = async (isRatingProvided: boolean, rating: BigInt) => {
        try{
            const storedInfo = localStorage.getItem(node.address);
            if(!storedInfo){
                console.error("No active connection info");
                return;
            }
            const connectionInfo = JSON.parse(storedInfo);
            if (isRatingProvided) {
                const nonce: BigInt = await getNextNonce(auth.providerWithInfo.provider, auth.accounts[0]);
                connectionInfo.clientSignature = await signMessage(nonce + connectionInfo.vpnClientPublicKey + connectionInfo.connectionStartTime + connectionInfo.agreedPricePerMinute + rating,auth.providerWithInfo.provider, auth.accounts[0]);
                localStorage.setItem(node.address,JSON.stringify(connectionInfo))
            }
            await processPayment(auth.providerWithInfo.provider, auth.accounts[0],
                node.address,
                connectionInfo.vpnClientPublicKey,
                BigInt(connectionInfo.connectionStartTime),
                BigInt(connectionInfo.agreedPricePerMinute),
                isRatingProvided,
                rating,
                connectionInfo.clientSignature,
                connectionInfo.nodeSignature
            );
            localStorage.removeItem(node.address);
        } catch (error) {
            console.error('Error:', error);
        }
    }

    const disconnect = async () => {
        // try{
        //     const nonce: BigInt = await getNextNonce(auth.providerWithInfo.provider, auth.accounts[0]);
        //     const sig = await signMessage(String(nonce),auth.providerWithInfo.provider, auth.accounts[0]);
        //     const res_string = String(nonce) + '\n' + sig;
        //     let response = await axios.post(getUrl() + "/disconnect", res_string);
        //     console.log(response.data);
        // } catch (error) {
        //     console.error('Error:', error);
        // }
        setRatingOpen(true);
    }

    const handleNoRating = () => {
        console.log("no rating");
        sendPayment(false, BigInt(0));
        handleCloseRating()
    }

    const handleCloseRating = () => {
        setRatingOpen(false);
        setRatingValue(null);
    };

    const handleSubmitRating = () => {
        if (ratingValue == null) {
            console.log("no rating value");
            sendPayment(false, BigInt(0));
            return;
        }
        console.log("submit rating:", ratingValue);
        setSubmittingRating(true);
        sendPayment(true, BigInt(ratingValue));
        setSubmittingRating(false);
        handleCloseRating();
    }

    return (
        <Grid size={{ xs: 12, sm: 6, md: 4 }}>
            <Card elevation={3} sx={{ height: '100%', display: 'flex', flexDirection: 'column', borderRadius: 2 }}>
                <CardContent sx={{ flexGrow: 1 }}>
                    <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
                        <Avatar sx={{ bgcolor: 'primary.main', mr: 2 }}>
                            <ServerIcon />
                        </Avatar>
                        <Typography variant="h6" component="div" noWrap>
                            {node.ip}
                        </Typography>
                    </Box>
                    
                    <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                        <SignalCellularAltIcon color="action" fontSize="small" />
                        <Typography variant="body2" color="text.secondary">
                            Traffic Load:
                        </Typography>
                        <Chip 
                            label={node.traffic} 
                            size="small" 
                            color={node.traffic > 5 ? "warning" : "success"} 
                            variant="outlined" 
                        />
                    </Box>
                    
                    <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                        <AttachMoneyIcon color="action" fontSize="small" />
                        <Typography variant="body2" color="text.secondary">
                            Price:
                        </Typography>
                        <Chip 
                            label={node.price} 
                            size="small" 
                            color={node.price > 15 ? "warning" : "success"} 
                            variant="outlined" 
                        />
                    </Box>

                    <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                        <StarIcon color="action" fontSize="small" />
                        <Typography variant="body2" color="text.secondary">
                            Rating:
                        </Typography>
                        <Chip 
                            label={node.rating} 
                            size="small" 
                            color={node.rating > 3 ? "success" : "warning"} 
                            variant="outlined" 
                        />
                    </Box>
                </CardContent>
                <CardActions sx={{ justifyContent: 'flex-end', p: 2, pt: 0 }}>
                    <ButtonGroup
                        disableElevation
                        variant="contained"
                        size="small"
                    >
                        <Button 
                            onClick={() => disconnect()} 
                            color="error" 
                            variant="outlined"
                        >
                            Disconnect
                        </Button>
                        <Button 
                            onClick={() => connect()}
                            color="primary"
                        >
                            Connect
                        </Button>
                    </ButtonGroup>
                </CardActions>
            </Card>

            <Dialog open={ratingOpen} onClose={handleNoRating}>
                <DialogTitle>Rate this server</DialogTitle>
                <DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2, minWidth: 320 }}>
                    <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
                        <Typography component="span">Your rating:</Typography>
                        <Rating
                            name={`server-rating-${node.ip}`}
                            value={ratingValue}
                            onChange={(_, newValue) => setRatingValue(newValue)}
                        />
                    </Box>
                </DialogContent>
                <DialogActions>
                    <Button onClick={handleNoRating} disabled={submittingRating}>Cancel</Button>
                    <Button onClick={handleSubmitRating} disabled={submittingRating || ratingValue == null} variant="contained">Submit</Button>
                </DialogActions>
            </Dialog>
        </Grid>
    );
}

export default function FolderList() {
    const { auth } = useAuth();
    const [nodes, setNodes] = React.useState<Node[]>([]);
    const [loading, setLoading] = React.useState(false);
    const fetchNodes = React.useCallback(async () => {
        setLoading(true);
        try {
            const provider = auth?.providerWithInfo?.provider;
            const fetchedNodes = await getActiveNodes(provider);
            setNodes(fetchedNodes);
        } catch (err) {
            console.error("Failed to fetch nodes", err);
        } finally {
            setLoading(false);
        }
    }, [auth]);

    useEffect(() => {
        fetchNodes();
    }, [fetchNodes]);
    // const [nodes, setNodes] = React.useState<Node[]>([
    //     { ip: "57.158.82.48", traffic: 5, price: 10, rating: 3 },
    //     { ip: "8.210.33.199", traffic: 3, price: 15, rating: 4 },
    //     { ip: "45.77.12.5", traffic: 7, price: 20, rating: 5 },
    //     { ip: "203.120.45.78", traffic: 2, price: 8, rating: 2 },
    //     { ip: "91.189.88.25", traffic: 6, price: 12, rating: 4 },
    //     { ip: "132.148.9.201", traffic: 9, price: 18, rating: 5 },
    //     { ip: "60.12.180.99", traffic: 4, price: 14, rating: 3 },
    //     { ip: "199.59.243.100", traffic: 1, price: 6, rating: 1 },
    //     { ip: "34.216.77.3", traffic: 8, price: 22, rating: 5 },
    //     { ip: "185.199.108.153", traffic: 5, price: 11, rating: 4 },
    //     { ip: "13.107.21.200", traffic: 7, price: 16, rating: 4 },
    //     { ip: "216.58.214.14", traffic: 3, price: 9, rating: 2 },
    //     { ip: "104.21.44.33", traffic: 10, price: 25, rating: 5 },
    //     { ip: "47.90.12.201", traffic: 2, price: 7, rating: 1 },
    //     { ip: "23.45.67.89", traffic: 6, price: 13, rating: 3 },
    //     { ip: "192.0.2.123", traffic: 4, price: 17, rating: 4 },
    // ]);

    return (
        <Box sx={{ flexGrow: 1, p: 3 }}>
            <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 3 }}>
                <Typography variant="h4" component="div" sx={{ fontWeight: 'bold' }}>
                    Available Servers
                </Typography>

                <Box>
                  <Tooltip title="Refresh servers">
                    <span>
                      <IconButton
                        onClick={fetchNodes}
                        disabled={loading || auth?.providerWithInfo === undefined}
                        color="primary"
                        aria-label="refresh servers"
                      >
                        {loading ? <CircularProgress size={20} /> : <RefreshIcon />}
                      </IconButton>
                    </span>
                  </Tooltip>
                </Box>
            </Box>
            <Grid container spacing={3}>
                {nodes.map((node, index) => (
                    <NodeItem key={index} node={node} auth={auth}/>
                ))}
            </Grid>
        </Box>
    );
}