import { HypermediaLink } from 'first-npm-package-nicule/core';
import { Inject, Injectable, InjectionToken } from '@angular/core';

export interface ApiScope {
    name: string;
    href: string;
}

export const API_SCOPES = new InjectionToken<() => Array<ApiScope>>('API_SCOPES');

@Injectable()
export class HrefFormatterProvider {
    constructor(@Inject(API_SCOPES) private apiScopes: () => Array<ApiScope> = () => []) { }

    getHrefFormatters(): Array<ApplicationScopeHrefFormatter> {
        return this.apiScopes()
            .map(({name, href: scopeBase}) => new ApplicationScopeHrefFormatter(name, scopeBase));
    }

    getHrefFormatterForScope(scope: string): ApplicationScopeHrefFormatter {
        return this.getHrefFormatters()
            .find(({ scopeName }) => scopeName === scope);
    }

    getHrefFormatterForHref(href: string): ApplicationScopeHrefFormatter {
        return this.getHrefFormatters()
            .find(formatter => {
                try {
                    formatter.makePathFromHref(href);

                    return true;
                } catch {
                    return false;
                }
            });
    }
}

export class ApplicationScopeHrefFormatter {
    constructor(public scopeName, private scopeBaseHref) { }

    makePathFromHref(href: string): string {
        const protocol = window.location.protocol;
        const scopeUrl = `${protocol}//${this.scopeBaseHref}`;

        const scopeCanFormat = href.startsWith(scopeUrl);

        if (!scopeCanFormat) {
            throw Error('Link provided is not within the application scope!');
        }

        return href.substring(scopeUrl.length);
    }

    makeHrefFromPath(path: string): string {
        if (path.startsWith('/')) {
            path = path.substring(1);
        }

        return `//${this.scopeBaseHref}/${path}`;
    }

    matchesLink(link: HypermediaLink): boolean {
        const rel = link.rel || [];

        return rel.includes(this.scopeName);
    }

    getScope(): string {
        return this.scopeName;
    }
}
