import { ColorState, SketchPicker } from 'react-color';
import { Id } from '../../fields/FieldSymbols';
import { BoolCast, Cast, StrCast } from '../../fields/Types';
import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { DocServer } from '../DocServer';
import { FontIconBox } from '../views/nodes/button/FontIconBox';
import { DragManager } from './DragManager';
import { GroupManager } from './GroupManager';
import { CheckBox } from '../views/search/CheckBox';
import { undoBatch } from './UndoManager';
import * as React from 'react';
import './SettingsManager.scss';
import './ReportManager.scss';
import { action, computed, observable, runInAction } from 'mobx';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { BsX } from 'react-icons/bs';
import { BiX } from 'react-icons/bi';
import { AiOutlineUpload } from 'react-icons/ai';
import { HiOutlineArrowLeft } from 'react-icons/hi';
import { Issue } from './reportManagerSchema';
import { observer } from 'mobx-react';
import { Doc } from '../../fields/Doc';
import { Networking } from '../Network';
import { MainViewModal } from '../views/MainViewModal';
import { Octokit } from '@octokit/core';
import { Button, IconButton } from '@mui/material';
import { Oval } from 'react-loader-spinner';
import Dropzone from 'react-dropzone';
import ReactLoading from 'react-loading';
import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import { theme } from '../theme';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;

enum ViewState {
    VIEW,
    CREATE,
}

@observer
export class ReportManager extends React.Component<{}> {
    public static Instance: ReportManager;
    @observable private isOpen = false;

    @observable private query = '';
    @action private setQuery = (q: string) => {
        this.query = q;
    };

    private octokit: Octokit;

    @observable viewState: ViewState = ViewState.VIEW;
    @action private setViewState = (state: ViewState) => {
        this.viewState = state;
    };
    @observable submitting: boolean = false;
    @action private setSubmitting = (submitting: boolean) => {
        this.submitting = submitting;
    };

    @observable
    public shownIssues: Issue[] = [];
    @action setShownIssues = action((issues: Issue[]) => {
        this.shownIssues = issues;
    });

    @observable selectedIssue: Issue | undefined = undefined;
    @action setSelectedIssue = action((issue: Issue | undefined) => {
        this.selectedIssue = issue;
    });

    @observable private mediaFiles: File[] = [];
    @action private setMediaFiles = (files: File[]) => {
        this.mediaFiles = files;
    };

    constructor(props: {}) {
        super(props);
        ReportManager.Instance = this;

        this.octokit = new Octokit({
            auth: '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',
        });
    }

    public close = action(() => (this.isOpen = false));
    public open = action(async () => {
        this.isOpen = true;
        if (this.shownIssues.length === 0) {
            try {
                // load in the issues if not already loaded
                const issues = (await this.getAllIssues()) as Issue[];
                this.setShownIssues(issues.filter(issue => issue.state === 'open'));
                // this.updateIssueSearch();
            } catch (err) {
                console.log(err);
            }
        }
    });

    @observable private bugTitle = '';
    @action setBugTitle = action((title: string) => {
        this.bugTitle = title;
    });
    @observable private bugDescription = '';
    @action setBugDescription = action((description: string) => {
        this.bugDescription = description;
    });
    @observable private bugType = '';
    @action setBugType = action((type: string) => {
        this.bugType = type;
    });
    @observable private bugPriority = '';
    @action setBugPriority = action((priortiy: string) => {
        this.bugPriority = priortiy;
    });

    private showReportIssueScreen = () => {
        this.setSelectedIssue(undefined);
    };

    private closeReportIssueScreen = () => {
        this.setSelectedIssue(undefined);
    };

    // private toGithub = false;
    // will always be set to true - no alterntive option yet
    private toGithub = true;

    private formatTitle = (title: string, userEmail: string) => `${title} - ${userEmail.replace('@brown.edu', '')}`;

    public async getAllIssues(): Promise<any[]> {
        const res = await this.octokit.request('GET /repos/{owner}/{repo}/issues', {
            owner: 'brown-dash',
            repo: 'Dash-Web',
        });

        // 200 status means success
        if (res.status === 200) {
            return res.data;
        } else {
            throw new Error('Error getting issues');
        }
    }

    // turns an upload link into a servable link
    // ex:
    // C: /Users/dash/Documents/GitHub/Dash-Web/src/server/public/files/images/upload_8008dbc4b6424fbff14da7345bb32eb2.png
    // -> http://localhost:1050/files/images/upload_8008dbc4b6424fbff14da7345bb32eb2_l.png
    private fileLinktoServerLink = (fileLink: string) => {
        const serverUrl = 'https://browndash.com/';

        const regex = 'public';
        const publicIndex = fileLink.indexOf(regex) + regex.length;

        const finalUrl = `${serverUrl}${fileLink.substring(publicIndex + 1).replace('.', '_l.')}`;
        return finalUrl;
    };

    public async reportIssue() {
        console.log(this.bugTitle);
        console.log('reporting issue');
        if (this.bugTitle === '' || this.bugDescription === '' || this.bugType === '' || this.bugPriority === '') {
            alert('Please fill out all required fields to report an issue.');
            return;
        }
        this.setSubmitting(true);

        console.log('to github');
        const links = await this.uploadFilesToServer();
        const formattedLinks = (links ?? []).map(this.fileLinktoServerLink);

        const req = await this.octokit.request('POST /repos/{owner}/{repo}/issues', {
            owner: 'brown-dash',
            repo: 'Dash-Web',
            title: this.formatTitle(this.bugTitle, Doc.CurrentUserEmail),
            body: `${this.bugDescription} \n\nfiles:\n${formattedLinks.join('\n')}`,
            labels: ['from-dash-app', this.bugType, this.bugPriority],
        });

        // 201 status means success
        if (req.status !== 201) {
            alert('Error creating issue on github.');
            // on error, don't close the modal
            return;
        }

        // if we're down here, then we're good to go. reset the fields.
        this.setBugTitle('');
        this.setBugDescription('');
        // this.toGithub = false;
        this.setFileLinks([]);
        this.setBugType('');
        this.setBugPriority('');
        this.setSubmitting(false);
        alert('Successfully submitted issue.');
        // this.close();
    }

    @observable public fileLinks: any = [];
    @action setFileLinks = action((links: any) => {
        this.fileLinks = links;
    });

    private getServerPath = (link: any) => {
        return link.result.accessPaths.agnostic.server;
    };

    private uploadFiles = (input: any) => {
        // keep null while uploading
        this.setFileLinks(null);
        // upload the files to the server
        if (input.files && input.files.length !== 0) {
            const fileArray: File[] = Array.from(input.files);
            Networking.UploadFilesToServer(fileArray.map(file => ({ file }))).then(links => {
                console.log('finshed uploading', links.map(this.getServerPath));
                this.setFileLinks((links ?? []).map(this.getServerPath));
            });
        }
    };

    private uploadFilesToServer = async () => {
        const links = await Networking.UploadFilesToServer(this.mediaFiles.map(file => ({ file })));
        console.log('finshed uploading', links.map(this.getServerPath));
        return (links ?? []).map(this.getServerPath);
        // this.setFileLinks((links ?? []).map(this.getServerPath));
    };

    private onDrop = (files: File[]) => {
        this.setMediaFiles(files);
    };

    private reportComponent = () => {
        if (this.viewState === ViewState.VIEW) {
            return this.viewIssuesComponent();
        } else {
            return this.reportIssueComponent();
        }
    };

    private viewIssuesComponent = () => {
        return (
            <div className="view-issues">
                <div className="left">
                    <div className="report-header">
                        <h2>Open Issues</h2>
                        <Button
                            variant="contained"
                            onClick={() => {
                                this.setViewState(ViewState.CREATE);
                            }}>
                            report issue
                        </Button>
                    </div>
                    <input
                        className="report-input"
                        type="text"
                        placeholder="Filter..."
                        onChange={e => {
                            this.setQuery(e.target.value);
                        }}
                        required
                    />
                    <div className="issues">
                        {this.shownIssues
                            .filter(issue => issue.title.toLowerCase().includes(this.query))
                            .map(issue => (
                                <IssueCard
                                    key={issue.number}
                                    issue={issue}
                                    onSelect={() => {
                                        this.setSelectedIssue(issue);
                                    }}
                                />
                            ))}
                    </div>
                </div>
                <div className="right">{this.selectedIssue ? <IssueView issue={this.selectedIssue} /> : <div>No issue selected</div>}</div>
                <IconButton sx={{ position: 'absolute', top: '4px', right: '4px' }} onClick={this.close}>
                    <BiX size="16px" />
                </IconButton>
            </div>
        );
    };

    private reportIssueComponent = () => {
        return (
            <div className="report-issue">
                <div className="report-header-vertical">
                    <Button
                        variant="text"
                        onClick={() => {
                            this.setViewState(ViewState.VIEW);
                        }}
                        sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
                        <HiOutlineArrowLeft color={theme.palette.primary.main} />
                        back to view
                    </Button>
                    <h2>Report an Issue</h2>
                </div>
                <div className="report-section">
                    <label className="report-label">Please provide a title for the bug</label>
                    <input className="report-input" value={this.bugTitle} type="text" placeholder="Title..." onChange={e => this.setBugTitle(e.target.value)} required />
                </div>
                <div className="report-section">
                    <label className="report-label">Please leave a description for the bug and how it can be recreated</label>
                    <textarea className="report-textarea" value={this.bugDescription} placeholder="Description..." onChange={e => this.setBugDescription(e.target.value)} required />
                </div>
                <div className="report-selects">
                    <select className="report-select" name="bugType" onChange={e => (this.bugType = e.target.value)}>
                        <option value="" disabled selected>
                            Type
                        </option>
                        <option className="report-opt" value="priority-low">
                            Bug
                        </option>
                        <option className="report-opt" value="priority-medium">
                            Poor Design or Cosmetic
                        </option>
                        <option className="report-opt" value="priority-high">
                            Poor Documentation
                        </option>
                    </select>
                    <select className="report-select" name="bigPriority" onChange={e => (this.bugPriority = e.target.value)}>
                        <option className="report-opt" value="" disabled selected>
                            Priority
                        </option>
                        <option value="priority-low">Low</option>
                        <option value="priority-medium">Medium</option>
                        <option value="priority-high">High</option>
                    </select>
                </div>
                <Dropzone
                    onDrop={this.onDrop}
                    accept={{
                        'image/png': ['.png'],
                        'image/jpg': ['.jpg'],
                        'image/jpeg': ['.jpeg'],
                        'video/mp4': ['.mp4'],
                        'video/mpeg': ['.mpeg'],
                        'video/webm': ['.webm'],
                        'audio/mpeg': ['.mp3'],
                        'audio/wav': ['.wav'],
                        'audio/ogg': ['.ogg'],
                    }}>
                    {({ getRootProps, getInputProps }) => (
                        <div {...getRootProps({ className: 'dropzone' })}>
                            <input {...getInputProps()} />
                            <div className="dropzone-instructions">
                                <AiOutlineUpload size={25} />
                                <p>Drop or select media that shows the bug (optional)</p>
                            </div>
                        </div>
                    )}
                </Dropzone>
                {this.mediaFiles.length > 0 && (
                    <div className="files">
                        <ul className="file-list">
                            {this.mediaFiles.map((file, i) => (
                                <li key={file.name} className="file-name">
                                    {file.name}
                                    <IconButton
                                        onClick={() => {
                                            this.setMediaFiles(this.mediaFiles.filter(f => f !== file));
                                        }}>
                                        <BiX />
                                    </IconButton>
                                </li>
                            ))}
                        </ul>
                    </div>
                )}

                <Button
                    variant="contained"
                    sx={{ fontSize: '14px', display: 'flex', alignItems: 'center', gap: '1rem' }}
                    onClick={() => {
                        this.reportIssue();
                    }}>
                    Submit
                    {this.submitting && <Oval height={20} width={20} color="#ffffff" visible={true} ariaLabel="oval-loading" secondaryColor="#ffffff87" strokeWidth={3} strokeWidthSecondary={3} />}
                </Button>
                <IconButton sx={{ position: 'absolute', top: '4px', right: '4px' }} onClick={this.close}>
                    <BiX size={'16px'} />
                </IconButton>
            </div>
        );
    };

    private get reportInterface() {
        const isReportingIssue = this.selectedIssue === null;

        return (
            <div className="settings-interface">
                <div className="issue-list-wrapper">
                    <h3>Current Issues</h3>
                    <input type="text" placeholder="search issues" onChange={e => this.setQuery(e.target.value)}></input>
                    <br />
                    {this.shownIssues.length === 0 ? (
                        <ReactLoading className="loading-center" />
                    ) : (
                        this.shownIssues.map(issue => (
                            <div className="issue-list" key={issue.number} onClick={() => this.setSelectedIssue(issue)}>
                                {issue.title}
                            </div>
                        ))
                    )}
                    {/* <div className="settings-user">
                        <button onClick={() => this.getAllIssues().then(issues => this.issues = issues)}>Poll Issues</button>
                    </div> */}
                </div>

                <div className="close-button" onClick={this.close}>
                    <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
                </div>

                <div className="issue-content" style={{ paddingTop: this.selectedIssue === undefined ? '50px' : 'inherit' }}>
                    {this.selectedIssue === undefined ? 'no issue selected' : this.renderIssue(this.selectedIssue)}
                </div>

                <div className="report-issue-fab">
                    <span className="report-disclaimer" hidden={!isReportingIssue}>
                        Note: issue reporting is not anonymous.
                    </span>
                    <button onClick={() => (isReportingIssue ? this.closeReportIssueScreen() : this.showReportIssueScreen())}>{isReportingIssue ? 'Cancel' : 'Report New Issue'}</button>
                </div>
            </div>
        );
    }

    private renderIssue = (issue: Issue) => {
        const isReportingIssue = issue === null;

        return isReportingIssue ? (
            // report issue
            <div className="settings-content">
                <h3 style={{ textDecoration: 'underline' }}>Report an Issue</h3>
                <label>Please leave a title for the bug.</label>
                <br />
                <input value={this.bugTitle} type="text" placeholder="title" onChange={e => this.setBugTitle(e.target.value)} required />
                {/* <TextField fullWidth type="text" placeholder="Title..." required onChange={e => this.setBugTitle(e.target.value)} /> */}
                <br />
                <label>Please leave a description for the bug and how it can be recreated.</label>
                <textarea value={this.bugDescription} placeholder="description" onChange={e => this.setBugDescription(e.target.value)} required />
                <br />
                {/* {<label>Send to github issues? </label>
                <input type="checkbox" onChange={(e) => this.toGithub = e.target.checked} />
                <br /> } */}

                <label>Please label the issue</label>
                <div className="flex-select">
                    <select name="bugType" onChange={e => (this.bugType = e.target.value)}>
                        <option value="" disabled selected>
                            Type
                        </option>
                        <option value="priority-low">Bug</option>
                        <option value="priority-medium">Poor Design or Cosmetic</option>
                        <option value="priority-high">Poor Documentation</option>
                    </select>

                    <select name="priority" onChange={e => (this.bugPriority = e.target.value)}>
                        <option value="" disabled selected>
                            Priority
                        </option>
                        <option value="priority-low">Low</option>
                        <option value="priority-medium">Medium</option>
                        <option value="priority-high">High</option>
                    </select>
                </div>

                <div>
                    <label>Upload media that shows the bug (optional)</label>
                    <input type="file" name="file" multiple accept="audio/*, video/*, image/*" onChange={e => this.uploadFiles(e.target)} />
                </div>
                <br />

                <button onClick={() => this.reportIssue()} disabled={this.fileLinks === null} style={{ backgroundColor: this.fileLinks === null ? 'grey' : '' }}>
                    {this.fileLinks === null ? 'Uploading...' : 'Submit'}
                </button>
            </div>
        ) : (
            // view issue
            <div className="issue-container">
                <h5 style={{ textAlign: 'left' }}>
                    <a href={issue.html_url} target="_blank">
                        Issue #{issue.number}
                    </a>
                </h5>
                <div className="issue-title">{issue.title}</div>
                <ReactMarkdown children={issue.body as string} className="issue-body" linkTarget={'_blank'} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} />
            </div>
        );
    };

    render() {
        return (
            <MainViewModal
                // contents={this.reportInterface}
                contents={this.reportComponent()}
                isDisplayed={this.isOpen}
                interactive={true}
                closeOnExternalClick={this.close}
                dialogueBoxStyle={{ width: 'auto', minWidth: '400px', height: '85vh', maxHeight: '90vh', background: '#ffffff', borderRadius: '8px' }}
            />
        );
    }
}

interface IssueCardProps {
    issue: Issue;
    onSelect: () => void;
}
const IssueCard = ({ issue, onSelect }: IssueCardProps) => {
    return (
        <div className="issue" onClick={onSelect}>
            <label className="issue-label">#{issue.number}</label>
            <h3 className="issue-title">{issue.title}</h3>
        </div>
    );
};

interface IssueViewProps {
    issue: Issue;
}

const IssueView = ({ issue }: IssueViewProps) => {
    return (
        <div className="issue-view">
            <span className="issue-label">
                Issue{' '}
                <a className="issue-link" href={issue.html_url} target="_blank">
                    #{issue.number}
                </a>
            </span>
            <h2 className="issue-title">{issue.title}</h2>
            <ReactMarkdown children={issue.body as string} className="issue-content" linkTarget={'_blank'} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} />
            {/* <p className="issue-content">{issue.body}</p> */}
        </div>
    );
};
