import { randUserName, randRecentDate, randIp, randPassword, randUuid, randNumber } from '@ngneat/falso';
import { r } from 'react-router/dist/development/fog-of-war-Cm1iXIp7';

export interface LoginAttempt {
    readonly date: string;
    readonly remoteIP: string;
    readonly username: string;
    readonly password: string;
    readonly sshClientVersion: string;
    readonly connectionUUID: string;
    readonly country: string;
}

export interface TotalStats {
    readonly password: number;
    readonly username: number;
    readonly ip: number;
    readonly country: number;
    readonly attempts: number;
}

export interface StatsResult {
    name: string;
    count: number;
}

export type StatsType = 'password' | 'username' | 'ip' | 'country' | 'attempts';

export interface ApiaryAPI {
    live(fn: (a: LoginAttempt) => void): void;
    stats(statsType: StatsType, limit: number): Promise<StatsResult[]>;
    query(queryType: string, query: string): Promise<LoginAttempt[]>;
    totals(): Promise<TotalStats>;
}

function fakeLoginAttempt(): LoginAttempt {
    return {
        date: randRecentDate({ days: 2 }).toISOString(),
        remoteIP: randIp().toString(),
        username: randUserName().toString(),
        password: randPassword().toString(),
        sshClientVersion: '1.0',
        connectionUUID: randUuid().toString(),
        country: 'NO'
    }
}

export class DummyApiaryAPIClient implements ApiaryAPI {
    live(fn: (a: LoginAttempt) => void): () => void {
        const interval = setInterval(() => {
            let a = fakeLoginAttempt();
            fn(a);
        }, 1000);

        return () => { clearInterval(interval) }
    }
    async stats(_type: StatsType, limit: number): Promise<StatsResult[]> {
        const stats = Array.from({ length: limit }, () => {
            switch (_type) {
                case 'password':
                    return { name: randPassword().toString(), count: randNumber().valueOf() }
                case 'username':
                    return { name: randUserName().toString(), count: randNumber().valueOf() }
                case 'ip':
                    return { name: randIp().toString(), count: randNumber().valueOf() }
                case 'country':
                    return { name: 'NO', count: randNumber().valueOf() }
            }
            return { name: randUserName().toString(), count: randNumber().valueOf() }
        });

        const sorted = stats.sort((a, b) => b.count - a.count)
        return Promise.resolve(sorted);
    }
    async query(_type: string, _query: string): Promise<LoginAttempt[]> {
        const attempts = Array.from({ length: 10 }, () => {
            return fakeLoginAttempt();
        })
        return Promise.resolve(attempts);
    }

    async totals(): Promise<TotalStats> {
        return Promise.resolve({ password: 1, username: 1, ip: 1, country: 1, attempts: 1 })
    }
}

export class ApiaryAPIClient implements ApiaryAPI {
    live(fn: (a: LoginAttempt) => void): () => void {
        const es = new EventSource('/api/stream');
        const updateFn = (ev: MessageEvent<string>) => {
            const attempt: LoginAttempt = JSON.parse(ev.data);
            fn(attempt);
        };
        es.addEventListener('message', updateFn)

        return () => {
            es.removeEventListener('message', updateFn);
            es.close();
        }
    }
    async stats(statsType: StatsType, limit: number): Promise<StatsResult[]> {
        const resp = await fetch(`/api/stats?type=${statsType}&limit=${limit}`)
        if (!resp.ok) {
            throw new Error('Failed to fetch query')
        }
        const data: StatsResult[] | null = await resp.json()
        if (!data) {
            return []
        }
        return data.sort((a, b) => b.count - a.count)
    }
    async query(queryType: string, query: string): Promise<LoginAttempt[]> {
        const resp = await fetch(`/api/query?type=${queryType}&query=${query}`)
        if (!resp.ok) {
            throw new Error('Failed to fetch query')
        }
        const data: LoginAttempt[] = await resp.json()

        return data
    }

    async totals(): Promise<TotalStats> {
        let password: number = -1
        let username: number = -1
        let ip: number = -1
        let attempts: number = -1
        let country: number = -1

        const resp = await fetch('/api/stats?type=total')
        const data: Array<StatsResult> = await resp.json()

        for (const stat of data) {
            switch (stat.name) {
                case 'UniquePasswords':
                    password = stat.count
                    break
                case 'UniqueUsernames':
                    username = stat.count
                    break
                case 'UniqueIPs':
                    ip = stat.count
                    break
                case 'TotalLoginAttempts':
                    attempts = stat.count
                    break
                case 'UniqueCountries':
                    country = stat.count
                    break
            }
        }

        return { password, username, ip, attempts, country }
    }
}
