import React, { useContext, useEffect, useState } from 'react';
import {
    Alert,
    Button,
    Card,
    Col,
    Container,
    Form,
    InputGroup,
    Row,
} from 'react-bootstrap';
import { ClientContext } from '../../contexts/clientProvider';
import { apiFetch } from '../../utils/helpers';
import Loading from '../bootstrap/Loading';
import { load } from '../../main';
import {
    InfinityMintScript,
    InfinityMintScriptArguments,
} from 'infinitymint/dist/app/interfaces';
import { Dictionary } from 'infinitymint/dist/app/helpers';
import Execute from './Execute';

export default function Scripts() {
    const client = useContext(ClientContext);

    const [loading, setLoading] = useState(true);
    const [scripts, setScripts] = useState<InfinityMintScript[]>([]);
    const [searchTerm, setSearchTerm] = useState<string>('');
    const [selectedScript, setSelectedScript] = useState<string>('');
    const [args, setArgs] = useState<Dictionary<{}>>({});

    useEffect(() => {
        if (!client.loaded || !client.controller?.apiAccess) return;

        let main = async () => {
            setLoading(true);
            let scripts = await apiFetch('/meta/scripts');
            setScripts(scripts.scripts);
            setLoading(false);
        };
        main();
    }, [client]);

    let tempScripts = [...scripts];
    let hidden = 0;

    if (searchTerm.length > 0) {
        tempScripts = tempScripts.filter((script) => {
            if (script.name.toLowerCase().includes(searchTerm.toLowerCase())) {
                return true;
            }
            hidden++;
            return false;
        });

        tempScripts.forEach((script) => {
            script.arguments.sort((a, b) => {
                if (a.optional && !b.optional) return 1;
                if (!a.optional && b.optional) return -1;
                return 0;
            });
        });
    }

    return (
        <Container>
            <Row>
                <Col className="m-3 border-bottom">
                    <h2>Scripts</h2>
                    <p>
                        You can use this page to execute scripts inside of
                        InfinityMint using the power of web sockets.
                    </p>
                </Col>
            </Row>
            <Row hidden={client?.controller?.apiAccess}>
                <Col>
                    <Alert variant="danger">
                        <Alert.Heading>
                            InfinityMint API not available
                        </Alert.Heading>
                        <p>
                            You will need connect to the InfinityMint API in
                            order to execute scripts!
                        </p>
                    </Alert>
                </Col>
            </Row>
            {loading ? (
                <Row>
                    <Col>
                        <Loading />
                    </Col>
                </Row>
            ) : (
                <>
                    {!selectedScript ? (
                        <>
                            <Row>
                                <Col>
                                    <InputGroup className="mb-3">
                                        <InputGroup.Text id="basic-addon3">
                                            Search Term
                                        </InputGroup.Text>
                                        <Form.Control
                                            id="text"
                                            onKeyDown={(e) => {
                                                if (e.key === 'Enter') {
                                                    if (
                                                        e.currentTarget
                                                            .value === ''
                                                    )
                                                        setSearchTerm('');

                                                    e.preventDefault();
                                                    e.stopPropagation();
                                                    return false;
                                                }

                                                return true;
                                            }}
                                            onChange={(e) => {
                                                setSearchTerm(e.target.value);
                                            }}
                                            aria-describedby="basic-addon3"
                                        />
                                        <Button
                                            variant="outline-secondary"
                                            onClick={() => {
                                                if (searchTerm === '') return;
                                                setSearchTerm('');
                                            }}
                                        >
                                            Search
                                        </Button>
                                    </InputGroup>
                                </Col>
                            </Row>
                            <Row hidden={searchTerm === ''}>
                                <Col>
                                    <Alert>
                                        <Alert.Heading>
                                            {tempScripts.length}{' '}
                                            {tempScripts.length === 1
                                                ? 'script'
                                                : 'scripts'}{' '}
                                            found
                                        </Alert.Heading>
                                        <p>
                                            {hidden}{' '}
                                            {hidden === 1
                                                ? 'script'
                                                : 'scripts'}{' '}
                                            hidden due to search term of "
                                            {searchTerm}"
                                        </p>
                                    </Alert>
                                </Col>
                            </Row>
                            <Row className="mb-4">
                                <Col lg={12}>
                                    <Row className="gy-4 row-cols-1">
                                        {tempScripts.map((script) => {
                                            return (
                                                <>
                                                    <Col className="d-grid">
                                                        <Card>
                                                            <Card.Header className="fs-4">
                                                                {script.name}
                                                            </Card.Header>
                                                            <Card.Body>
                                                                {
                                                                    script.description
                                                                }

                                                                {script.arguments
                                                                    ? script.arguments.map(
                                                                          (
                                                                              argument
                                                                          ) => {
                                                                              return (
                                                                                  <Row className="mt-2">
                                                                                      <Col>
                                                                                          {argument.optional ? (
                                                                                              <span className="badge bg-secondary mb-2">
                                                                                                  Optional
                                                                                              </span>
                                                                                          ) : (
                                                                                              <span className="badge bg-danger mb-2">
                                                                                                  Required
                                                                                              </span>
                                                                                          )}

                                                                                          {argument.type !==
                                                                                          'boolean' ? (
                                                                                              <input
                                                                                                  type="text"
                                                                                                  className="form-control"
                                                                                                  onChange={(
                                                                                                      e
                                                                                                  ) => {
                                                                                                      let tempArgs =
                                                                                                          {
                                                                                                              ...args,
                                                                                                          };

                                                                                                      if (
                                                                                                          !tempArgs[
                                                                                                              script
                                                                                                                  .name
                                                                                                          ]
                                                                                                      )
                                                                                                          tempArgs[
                                                                                                              script.name
                                                                                                          ] =
                                                                                                              {};

                                                                                                      tempArgs[
                                                                                                          script.name
                                                                                                      ][
                                                                                                          argument.name
                                                                                                      ] =
                                                                                                          e.target.value;

                                                                                                      setArgs(
                                                                                                          tempArgs
                                                                                                      );
                                                                                                  }}
                                                                                                  value={
                                                                                                      argument?.value
                                                                                                  }
                                                                                                  placeholder={
                                                                                                      argument.name
                                                                                                  }
                                                                                              />
                                                                                          ) : (
                                                                                              <>
                                                                                                  <br />
                                                                                                  <label
                                                                                                      className="form-label"
                                                                                                      style={{
                                                                                                          textDecoration:
                                                                                                              'underline',
                                                                                                      }}
                                                                                                  >
                                                                                                      {
                                                                                                          argument.name
                                                                                                      }
                                                                                                  </label>
                                                                                                  <select
                                                                                                      className="form-select"
                                                                                                      onChange={(
                                                                                                          e
                                                                                                      ) => {
                                                                                                          let tempArgs =
                                                                                                              {
                                                                                                                  ...args,
                                                                                                              };

                                                                                                          if (
                                                                                                              !tempArgs[
                                                                                                                  script
                                                                                                                      .name
                                                                                                              ]
                                                                                                          )
                                                                                                              tempArgs[
                                                                                                                  script.name
                                                                                                              ] =
                                                                                                                  {};

                                                                                                          tempArgs[
                                                                                                              script.name
                                                                                                          ][
                                                                                                              argument.name
                                                                                                          ] =
                                                                                                              e
                                                                                                                  .target
                                                                                                                  .value ===
                                                                                                              'true';

                                                                                                          setArgs(
                                                                                                              tempArgs
                                                                                                          );
                                                                                                      }}
                                                                                                  >
                                                                                                      <option
                                                                                                          value="true"
                                                                                                          selected={
                                                                                                              argument.value ===
                                                                                                              true
                                                                                                          }
                                                                                                      >
                                                                                                          true
                                                                                                      </option>
                                                                                                      <option
                                                                                                          value="false"
                                                                                                          selected={
                                                                                                              argument.value ===
                                                                                                              false
                                                                                                          }
                                                                                                      >
                                                                                                          false
                                                                                                      </option>
                                                                                                  </select>
                                                                                              </>
                                                                                          )}
                                                                                      </Col>
                                                                                  </Row>
                                                                              );
                                                                          }
                                                                      )
                                                                    : null}
                                                            </Card.Body>
                                                            <Card.Footer>
                                                                <Button
                                                                    onClick={() => {
                                                                        setSelectedScript(
                                                                            script.name
                                                                        );
                                                                    }}
                                                                    variant="success"
                                                                >
                                                                    Execute
                                                                </Button>
                                                            </Card.Footer>
                                                        </Card>
                                                    </Col>
                                                </>
                                            );
                                        })}
                                    </Row>
                                </Col>
                                <Col></Col>
                            </Row>
                        </>
                    ) : (
                        <>
                            <Row>
                                <Col>
                                    <Execute
                                        script={selectedScript}
                                        args={(() => {
                                            let selectedArgs =
                                                args[selectedScript] || {};
                                            let defaultScriptArgs =
                                                scripts.find(
                                                    (script) =>
                                                        script.name ===
                                                        selectedScript
                                                ).arguments;

                                            if (selectedArgs) {
                                                return defaultScriptArgs.map(
                                                    (arg) => {
                                                        if (
                                                            selectedArgs[
                                                                arg.name
                                                            ] === undefined
                                                        ) {
                                                            return {
                                                                ...arg,
                                                            };
                                                        }

                                                        return {
                                                            ...arg,
                                                            value: selectedArgs[
                                                                arg.name
                                                            ],
                                                        };
                                                    }
                                                );
                                            }

                                            return Object.values(selectedArgs);
                                        })()}
                                    />
                                </Col>
                            </Row>
                            <Row className="mt-4 px-4">
                                <Col>
                                    <div className="d-grid">
                                        <Button
                                            variant="outline-secondary"
                                            onClick={() => {
                                                setSelectedScript(null);
                                            }}
                                        >
                                            Back
                                        </Button>
                                    </div>
                                </Col>
                            </Row>
                        </>
                    )}
                </>
            )}
        </Container>
    );
}
