

export type ModuleAddress =
    ModuleAddress.GitHubRepo |
    ModuleAddress.GitHubRawUrl |
    ModuleAddress.DenoLandUrl
    ;

export namespace ModuleAddress {

    /** e.g: "github:userOrOrg/repositoryName#branch" */
    export type GitHubRepo = {
        type: "GITHUB REPO";
        userOrOrg: string;
        repositoryName: string;
        branch?: string;
    };

    export namespace GitHubRepo {
        /**
         * Input example 
         * KSXGitHub/simple-js-yaml-port-for-deno or
         * github:KSXGitHub/simple-js-yaml-port-for-deno or
         * KSXGitHub/simple-js-yaml-port-for-deno#3.12.1 or
         * github:KSXGitHub/simple-js-yaml-port-for-deno#3.12.1 or
         */
        export function parse(
            raw: string
        ): GitHubRepo {

            const match = raw.match(/^(?:github:\s*)?([^\/]*)\/([^\/]+)$/i)!;

            const [repositoryName, branch] = match[2].split("#");

            return {
                "type": "GITHUB REPO",
                "userOrOrg": match[1],
                repositoryName,
                branch
            };
        }

        /** Match valid parse input */
        export function match(raw: string): boolean {
            return /^(?:github:)?[^\/\:]+\/[^\/]+$/.test(raw);
        }
    }

    /** https://raw.githubusercontent.com/user/repo/branch/path/to/file.ts */
    export type GitHubRawUrl = {
        type: "GITHUB-RAW URL"
        baseUrlWithoutBranch: string;
        pathToIndex: string;
        branch: string;
    };

    export namespace GitHubRawUrl {

        export function parse(raw: string): GitHubRawUrl {

            const match = raw.match(
                /^(https?:\/\/raw\.github(?:usercontent)?\.com\/[^\/]+\/[^\/]+\/)([^\/]+)\/(.*)$/
            )!;

            return {
                "type": "GITHUB-RAW URL",
                "baseUrlWithoutBranch": match[1]
                    .replace(
                        /^https?:\/\/raw\.github(?:usercontent)?/,
                        "https://raw.githubusercontent"
                    )
                    .replace(/\/$/, "")
                ,
                "branch": match[2],
                "pathToIndex": match[3]
            };

        }

        export function match(raw: string): boolean {
            return /^https?:\/\/raw\.github(?:usercontent)?\.com/.test(raw);
        }

    }

    /** e.g: https://deno.land/x/foo@1.2.3/mod.js */
    export type DenoLandUrl = {
        type: "DENO.LAND URL"
        isStd: boolean;
        baseUrlWithoutBranch: string;
        pathToIndex: string;
        branch?: string;
    };

    export namespace DenoLandUrl {

        export function parse(raw: string): DenoLandUrl {

            const isStd = /^https?:\/\/deno\.land\/std/.test(raw);

            const match = isStd ?
                raw.match(/^(https?:\/\/deno\.land\/std)([@\/].*)$/)! :
                raw.match(/^(https?:\/\/deno\.land\/x\/[^@\/]+)([@\/].*)$/)!
                ;

            // https://deno.land/std@master/node/querystring.ts
            // [1]: https://deno.land/std
            // [2]: @master/node/querystring.ts

            // https://deno.land/std/node/querystring.ts
            // [1]: https://deno.land/std
            // [2]: /node/querystring.ts

            //https://deno.land/x/foo@1.2.3/mod.js
            // [1]: https://deno.land/x/foo
            // [2]: @1.2.3/mod.js

            //https://deno.land/x/foo/mod.js
            // [1]: https://deno.land/x/foo
            // [2]: /mod.js

            const { branch, pathToIndex } = match[2].startsWith("@") ? (() => {

                const [
                    , branch, // 1.2.3
                    pathToIndex // mod.js
                ] = match[2].match(/^@([^\/]+)\/(.*)$/)!;

                return { branch, pathToIndex }

            })() : ({
                "branch": undefined,
                "pathToIndex": match[2].replace(/^\//, "") // mod.js
            });

            return {
                "type": "DENO.LAND URL",
                isStd,
                "baseUrlWithoutBranch": match[1],
                "branch": branch,
                pathToIndex
            };

        }

        export function match(raw: string): boolean {
            return /^https?:\/\/deno\.land\/(?:(?:std)|(?:x))[\/|@]/.test(raw);
        }
    }

    export function parse(raw: string): ModuleAddress {

        for (const ns of [GitHubRepo, GitHubRawUrl, DenoLandUrl]) {

            if (!ns.match(raw)) {
                continue;
            }

            return ns.parse(raw);

        }

        throw new Error(`${raw} is not a valid module address`);


    }



}
