import { Service } from '../service';
import { {{ spec.title | caseUcfirst}}Exception, Client } from '../client';
import type { Models } from '../models';
import type { UploadProgress, Payload } from '../client';
import * as FileSystem from 'expo-file-system';
import { Platform } from 'react-native';

{% set added = [] %}
{% for method in service.methods %}
{% for parameter in method.parameters.all %}
{% if parameter.enumValues is not empty %}
{% if parameter.enumName is not empty %}
{% set name = parameter.enumName %}
{% else %}
{% set name = parameter.name %}
{% endif %}
{% if name not in added %}
import { {{ name | caseUcfirst }} } from '../enums/{{ name | caseDash }}';
{% set added = added|merge([name]) %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}

export class {{ service.name | caseUcfirst }} extends Service {

     constructor(client: Client)
     {
        super(client);
     }
{% for method in service.methods %}

    /**
     * {{ method.title }}
     *
{% if method.description %}
{{ method.description|comment2 }}
{% endif %}
     *
{% for parameter in method.parameters.all %}
     * @param {{ '{' }}{{ parameter | getPropertyType(method) | raw }}{{ '}' }} {{ parameter.name | caseCamel | escapeKeyword }}
{% endfor %}
     * @throws {{ '{' }}{{ spec.title | caseUcfirst}}Exception}
     * @returns {% if method.type == 'webAuth' %}{void|string}{% elseif method.type == 'location' %}{URL}{% else %}{Promise}{% endif %}

    */
    {% if method.type != 'location' and method.type != 'webAuth'%}async {% endif %}{{ method.name | caseCamel }}{{ method.responseModel | getGenerics(spec) | raw }}({% for parameter in method.parameters.all %}{{ parameter.name | caseCamel | escapeKeyword }}{% if not parameter.required or parameter.nullable %}?{% endif %}: {{ parameter | getPropertyType(method) | raw }}{% if not loop.last %}, {% endif %}{% endfor %}{% if 'multipart/form-data' in method.consumes %}, onProgress = (progress: UploadProgress) => {}{% endif %}): {{ method | getReturn(spec) | raw }} {
{% for parameter in method.parameters.all %}
{% if parameter.required %}
        if (typeof {{ parameter.name | caseCamel | escapeKeyword }} === 'undefined') {
            throw new {{spec.title | caseUcfirst}}Exception('Missing required parameter: "{{ parameter.name | caseCamel | escapeKeyword }}"');
        }

{% endif %}
{% endfor %}
        const apiPath = '{{ method.path }}'{% for parameter in method.parameters.path %}.replace('{{ '{' }}{{ parameter.name | caseCamel | escapeKeyword }}{{ '}' }}', {{ parameter.name | caseCamel | escapeKeyword }}){% endfor %};
        const payload: Payload = {};

{% for parameter in method.parameters.query %}
        if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') {
            payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }};
        }

{% endfor %}
{% for parameter in method.parameters.body %}
        if (typeof {{ parameter.name | caseCamel | escapeKeyword }} !== 'undefined') {
            payload['{{ parameter.name }}'] = {{ parameter.name | caseCamel | escapeKeyword }};
        }

{% endfor %}
        const uri = new URL(this.client.config.endpoint + apiPath);
{% if method.type == 'location' or method.type == 'webAuth' %}
{% if method.auth|length > 0 %}
{% for node in method.auth %}
{% for key,header in node|keys %}
        payload['{{header|caseLower}}'] = this.client.config.{{header|caseLower}};

{% endfor %}
{% endfor %}
{% endif %}

        for (const [key, value] of Object.entries(Service.flatten(payload))) {
            uri.searchParams.append(key, value);
        }
{% endif %}
{% if method.type == 'webAuth' %}
        return uri;
{% elseif method.type == 'location' %}
        return uri;
{% else %}
{% if 'multipart/form-data' in method.consumes %}
{% for parameter in method.parameters.all %}
{% if parameter.type == 'file' %}

        const size = {{ parameter.name | caseCamel | escapeKeyword }}.size;

        if (size <= Service.CHUNK_SIZE) {
            return await this.client.call('{{ method.method | caseLower }}', uri, {
{% for parameter in method.parameters.header %}
                '{{ parameter.name | caseCamel | escapeKeyword }}': this.client.${{ parameter.name | caseCamel | escapeKeyword }},
{% endfor %}
{% for key, header in method.headers %}
                '{{ key }}': '{{ header }}',
{% endfor %}
            }, payload);
        }

        const apiHeaders: { [header: string]: string } = {
{% for parameter in method.parameters.header %}
            '{{ parameter.name | caseCamel | escapeKeyword }}': this.client.${{ parameter.name | caseCamel | escapeKeyword }},
{% endfor %}
{% for key, header in method.headers %}
            '{{ key }}': '{{ header }}',
{% endfor %}
        }

        let offset = 0;
        let response = undefined;
{% for parameter in method.parameters.all %}
{% if parameter.isUploadID %}
        if({{ parameter.name | caseCamel | escapeKeyword }} != 'unique()') {
            try {
                response = await this.client.call('GET', new URL(this.client.config.endpoint + apiPath + '/' + {{ parameter.name }}), apiHeaders);
                offset = response.chunksUploaded * Service.CHUNK_SIZE;
            } catch(e) {
            }
        }
{% endif %}
{% endfor %}

        let timestamp = new Date().getTime();
        while (offset < size) {
            let end = Math.min(offset + Service.CHUNK_SIZE - 1, size - 1);

            apiHeaders['content-range'] = 'bytes ' + offset + '-' + end + '/' + size;
            if (response && response.$id) {
                apiHeaders['x-{{spec.title | caseLower }}-id'] = response.$id;
            }

            let chunk = await FileSystem.readAsStringAsync({{ parameter.name | caseCamel | escapeKeyword }}.uri, {
                encoding: FileSystem.EncodingType.Base64,
                position: offset,
                length: Service.CHUNK_SIZE
            });
            var path = `data:${{'{'}}{{ parameter.name | caseCamel | escapeKeyword }}.type{{'}'}};base64,${{'{'}}chunk{{'}'}}`;
            if (Platform.OS.toLowerCase() === 'android') {
                path = FileSystem.cacheDirectory + '/tmp_chunk_' + timestamp;
                await FileSystem.writeAsStringAsync(path, chunk, {encoding: FileSystem.EncodingType.Base64});
            }

            payload['{{ parameter.name }}'] = {{ '{' }} uri: path, name: {{ parameter.name | caseCamel | escapeKeyword }}.name, type: {{ parameter.name | caseCamel | escapeKeyword }}.type {{ '}' }};

            response = await this.client.call('{{ method.method | caseLower }}', uri, apiHeaders, payload);

            if (onProgress) {
                onProgress({
                    $id: response.$id,
                    progress: (offset / size) * 100,
                    sizeUploaded: offset,
                    chunksTotal: response.chunksTotal,
                    chunksUploaded: response.chunksUploaded
                });
            }
            offset += Service.CHUNK_SIZE;
        }
        return response;
{% endif %}
{% endfor %}
{% else %}
        return await this.client.call('{{ method.method | caseLower }}', uri, {
{% for parameter in method.parameters.header %}
            '{{ parameter.name | caseCamel | escapeKeyword }}': this.client.${{ parameter.name | caseCamel | escapeKeyword }},
{% endfor %}
{% for key, header in method.headers %}
            '{{ key }}': '{{ header }}',
{% endfor %}
        }, payload);
{% endif %}
{% endif %}
    }
{% endfor %}
};
