import * as t from '@babel/types';
import { GenericParseContext } from '../../../encoding';
import { objectPattern } from '../../../utils';
import { rpcFuncArguments, rpcClassArguments, rpcRecursiveObjectProps } from './rpc';
import { restoreExtension } from '@cosmology/utils';

export const grpcWebClientScaffold = (): t.Statement[] => {
    return [
        t.expressionStatement(
            t.assignmentExpression(
                "=",
                t.identifier('endpoint'),
                t.callExpression(
                    t.memberExpression(
                        t.identifier('endpoint'),
                        t.identifier('replace')
                    ),
                    [
                        t.regExpLiteral(
                            "\\/*$"
                        ),
                        t.stringLiteral(
                            ""
                        )
                    ]
                )
            )
        ),
        t.variableDeclaration(
            "const",
            [
                t.variableDeclarator(
                    t.objectPattern(
                        [
                            t.objectProperty(
                                t.identifier("GrpcWebImpl"),
                                t.identifier("GrpcWebImpl"),
                                false,
                                true
                            )
                        ]
                    ),
                    t.awaitExpression(
                        t.callExpression(
                            t.import(),
                            [
                                t.stringLiteral(
                                    "../cosmos/app/v1alpha1/query.rpc.Query"
                                )
                            ]
                        )
                    )
                )
            ]
        ),
        t.variableDeclaration(
            "let",
            [
                t.variableDeclarator(
                    t.identifier('grpcWeb')
                ),

            ]
        ),
        t.ifStatement(
            t.binaryExpression(
                "!==",
                t.unaryExpression(
                    "typeof",
                    t.identifier('document'),
                    true
                ),
                t.stringLiteral(
                    "undefined"
                )
            ),
            t.blockStatement(
                [
                    t.expressionStatement(
                        t.assignmentExpression(
                            "=",
                            t.identifier("grpcWeb"),
                            t.newExpression(
                                t.identifier("GrpcWebImpl"),
                                [
                                    t.identifier("endpoint"),
                                    t.objectExpression(
                                        [
                                            t.objectProperty(
                                                t.identifier("transport"),
                                                t.callExpression(
                                                    t.memberExpression(
                                                        t.identifier("grpc"),
                                                        t.identifier("CrossBrowserHttpTransport")
                                                    ),
                                                    [
                                                        t.objectExpression(
                                                            [
                                                                t.objectProperty(
                                                                    t.identifier("withCredentials"),
                                                                    t.booleanLiteral(
                                                                        false
                                                                    )
                                                                )
                                                            ]
                                                        )
                                                    ]
                                                )
                                            )
                                        ]
                                    )
                                ]
                            )
                        )
                    )
                ]
            ),
            t.ifStatement(
                t.logicalExpression(
                    "&&",
                    t.binaryExpression(
                        "!==",
                        t.unaryExpression(
                            "typeof",
                            t.identifier("navigator"),
                            true
                        ),
                        t.stringLiteral(
                            "undefined"
                        )
                    ),
                    t.binaryExpression(
                        "===",
                        t.memberExpression(
                            t.identifier("navigator"),
                            t.identifier("product")
                        ),
                        t.stringLiteral(
                            "ReactNative"
                        )
                    )
                ),
                t.blockStatement(
                    [
                        t.expressionStatement(
                            t.assignmentExpression(
                                "=",
                                t.identifier("grpcWeb"),
                                t.newExpression(
                                    t.identifier("GrpcWebImpl"),
                                    [
                                        t.identifier("endpoint"),
                                        t.objectExpression(
                                            [
                                                t.objectProperty(
                                                    t.identifier("transport"),
                                                    t.callExpression(
                                                        t.identifier("NodeHttpTransport"),
                                                        []
                                                    )
                                                )
                                            ]
                                        )
                                    ]
                                )
                            )
                        )
                    ]
                ),
                t.blockStatement(
                    [
                        t.expressionStatement(
                            t.assignmentExpression(
                                "=",
                                t.identifier("grpcWeb"),
                                t.newExpression(
                                    t.identifier("GrpcWebImpl"),
                                    [
                                        t.identifier("endpoint"),
                                        t.objectExpression(
                                            [
                                                t.objectProperty(
                                                    t.identifier("transport"),
                                                    t.callExpression(
                                                        t.identifier("NodeHttpTransport"),
                                                        []
                                                    )
                                                )
                                            ]
                                        )
                                    ]
                                )
                            )
                        )
                    ]
                )
            )
        ),
    ];
};

export const grpcFuncArguments = (): t.ObjectPattern[] => {
    return [
        objectPattern([
            t.objectProperty(
                t.identifier('endpoint'),
                t.identifier('endpoint'),
                false,
                true
            )
        ],
        t.tsTypeAnnotation(
            t.tsTypeLiteral(
                [
                    t.tsPropertySignature(
                        t.identifier('endpoint'),
                        t.tsTypeAnnotation(
                            t.tsStringKeyword()
                        )
                    )
                ]
            )
            )
        )
    ];
};

export const grpcWebNewAwaitImport = (
    path: string,
    className: string,
    options?: {
      restoreImportExtension?: string;
    }
) => {
    return t.newExpression(
        t.memberExpression(
            t.awaitExpression(
                t.callExpression(
                    t.import(),
                    [
                        t.stringLiteral(
                            restoreExtension(path, options?.restoreImportExtension)
                        )
                    ]
                )
            ),
            t.identifier(className),
            false
        ),
        [
            t.identifier('grpcWeb')
        ]
    )
}

export const grpcNestedImportObject = (
    obj: object,
    className: string,
    options?: {
      restoreImportExtension?: string;
    }
) => {

    //make className dynamic based on object
    if (typeof obj === 'string') {
        const serviceType = (obj as string).split(".").pop();
        switch (serviceType) {
            case "Query":
            //   console.log("This is a Query RPC.");
              break;
            case "Service":
              className = 'ServiceClientImpl';
            //   console.log("This is a Service RPC.");
              break;
            case "msg":
                className = 'MsgClientImpl';
            //   console.log("This is a Message RPC.");
              break;
            default:
              console.log("grpc service error!! This should not happend. Undefined service type");
          }
        return grpcWebNewAwaitImport(obj, className, options);
    }

    const keys = Object.keys(obj);

    return t.objectExpression(keys.map(name => {
        return t.objectProperty(
            t.identifier(name),
            grpcNestedImportObject(obj[name], className, options)
        )
    }))
};

export const createScopedGrpcWebFactory = (
    context: GenericParseContext,
    obj: object,
    identifier: string
) => {

    context.addUtil('grpc');
    context.addUtil('NodeHttpTransport');

    return t.exportNamedDeclaration(
        t.variableDeclaration(
            'const',
            [
                t.variableDeclarator(
                    // createGrpcWebQueryClient
                    t.identifier(identifier),
                    t.arrowFunctionExpression(
                        grpcFuncArguments(),
                        t.blockStatement(
                            grpcWebClientScaffold().concat(
                            t.returnStatement(
                                grpcNestedImportObject(
                                    obj,
                                    'QueryClientImpl',
                                    context.options
                                )
                            ))
                        ),
                        true
                    )
                )
            ]
        )
    )
}

export const createScopedGrpcWebMsgFactory = (
    obj: object,
    identifier: string,
    className: string,
    options?: {
      restoreImportExtension?: string;
    }
) => {
    return t.exportNamedDeclaration(
        t.variableDeclaration(
            'const',
            [
                t.variableDeclarator(
                    t.identifier(identifier),
                    t.arrowFunctionExpression(
                        grpcFuncArguments(),
                        //
                        t.blockStatement(
                            grpcWebClientScaffold().concat(
                            t.returnStatement(
                                grpcNestedImportObject(
                                    obj,
                                    className,
                                    options
                                )
                            ))
                        ),
                        true
                    )
                )
            ]
        )
    )
}
