package tst

import (
	. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common"
	TS "git.eugeniocarvalho.dev/eugeniucarvalho/gg/generators/typescript"
)

func createAuthClass(p *Project, file *TS.File) {
	// auth := p.Auth.Oauth2
	// scope, _ := json.Marshal(auth.Client.Scope)

	file.Line().Comment(
		"Class gerencia autenticacao e autorizacao da api.",
	).Export().Class().Id("Auth").Block(TS.Raw(`

    // public profile = new Subject<any>();
    public profile$ = new Subject<any>();
    public profile: any;
    public cookieDomain: string;

    public grant: any;
    public onAuthorize$ = new BehaviorSubject(null);
    public clientParams = {
        redirect_uri: '',
        client_id: '',
        client_secret: '',
        scope: [],
        state: '',
    };
    
    protected oauthURI: string;

    // constructor(protected api: `).Id(ApiClassName(p)).Raw(`) {
    constructor(protected api: ApiInterface) {
        const options = api.apiOptions;
        this.oauthURI = options.ACCOUNT_URL;
        this.clientParams = {
            redirect_uri: options.REDIRECT_URI,
            client_id: options.CLIENT_ID,
            client_secret: options.CLIENT_SECRET,
            scope: options.SCOPES,
            state: this.state()
        };
        this.cookieDomain = options.COOKIE_DOMAIN;
        console.log("create auth instance", options);
    }
    protected state() {
        // return [32, 7, 28].map(k => { return Math.random().toString(k).substr(2) }).join('+');
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
            const r = Math.random() * 16 | 0;
            const v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }
    getProfile() {
        if (this.profile) {
            return of(this.profile).pipe(take(1));
        }
        return this.api.http
            .post<any>(`).Raw("`${this.oauthURI}/api/profile`").Raw(`, {}, this.api.options())
            .pipe(
                take(1),
                map(profile => this.profile = profile),
            );
    }
    Authorize(): Observable<any> {
        return this.getAuthCode().pipe(
            take(1),
            switchMap(code => {
                // console.log('(get code)', code);
                return this.getAccessToken({ code });
            }),
            catchError(err => {
                console.log('(catch erro grant)', err);
                switch (err) {
                case '':
                  return this.getAccessToken({
                    request_type: 'refreshToken',
                    refresh_token: this.grant.refresh_token
                  });
                case '':
                  err = { type: err };
                }
                throw err;
            }),
            tap(grant => {
                // console.log('(set grant )', grant);
                this.setGrant(grant);
                // this.api.poolReady.map(callback => callback());
                this.onAuthorize$.next(grant);
            }),
        );
    }
    Token() {
        const g = this.grant;
        if (g) {
            return `).Raw("`${g.token_type} ${g.access_token}`;").Raw(`
        }
        return '';
    }
    Unauthorize() {
        // ${this.oauthURI}/api/signout
        window.location.href = `).Raw("this.location('/api/signout').href").Raw(`;
    }
    protected getAccessToken(options: any) {

        if (this.grant) {
            return of(this.grant);
        }

        const params = Object.assign({
            request_type: 'accessToken',
            client_id: this.clientParams.client_id,
            state: this.clientParams.state
        }, options);

        return this.api.http.post<any>(`).Raw("`${this.oauthURI}/oauth2/token`").Raw(`, {},
            Object.assign({}, this.api.httpOptions, { params: params })
        );
    }
    protected setGrant(grant) {
        this.grant = grant;
        // recupera os dados do usuario
        setTimeout(() => this.getProfile().subscribe(data => this.profile$.next(data)));
    }
    protected location(path = '/oauth2/authorize'): URL {
        // Solicita autorização par ao servidor de autenticacao caso não exista um token e nem um codigo
        const url = new URL(`).Raw("`${this.oauthURI}${path}`").Raw(`);
        Object.keys(this.clientParams).map((k, v) => {
            url.searchParams.append(k, this.clientParams[k]);
        });
        return url;
    }
    protected getAuthCode() {
        return new Observable<string>(ob => {
            const oauth2 = document.createElement('iframe'),
                url = this.location();

            oauth2.id = `).Raw("`iframe-${Math.random().toString(36)}`").Raw(`;

            url.searchParams.set('redirect_uri', oauth2.id);
            
            function listenAccessCode(event) {
                // Check sender origin to be trusted
                // if (event.origin !== "http://example.com") return;
                const { type, code } = event.data;
                if (type === 'access_code') {
                  // remove o iframe do dom
                  oauth2.remove();
                  // submete o codigo
                  if (code) { ob.next(code); } else { ob.error(event.data); }

                  window.removeEventListener('message', listenAccessCode, false);
                }
              }
              window.addEventListener('message', listenAccessCode, false);
              
              oauth2.src = url.href;
              
              document.body.appendChild(oauth2);

              oauth2.onload = () => {
                if (!oauth2.contentDocument) {
                  window.postMessage({ 'type': 'access_code', 'error': 'unauthorized' }, '*');
                }
              }
            
        });
    }
	`))
}

        // return new Observable<any>(observer => {
        //     // Verifica se existe um usuario logado
        //     // Quando o usuario não está logado ele é redirecionado para o servidor de autenticacao
        //     if (!(`).Raw(fmt.Sprintf("/\\s%s=([^;]+);?/gm.exec(", p.Auth.AuthTokenID)).Raw("` ${document.cookie}`").Raw(`))) {
        //         window.location.href = this.location('/signin').href;
        //     }

        //     const res = (g, success = true) => {
        //         let f;
        //         if (success) {
        //             observer.next(g);
        //             while (f = this.api.poolReady.shift()){
        //                 f();
        //             }
        //         } else {
        //             observer.error(g);
        //         }
        //         observer.complete();
        //     };
        //     // Se existe um token de authorizacao
        //     if (this.grant) {
        //         this.setGrant(this.grant);
        //         res(this.grant);
        //         return;
        //     }
        //     this.getAuthCode().subscribe(
        //         code => {
        //             const done = (grant) => {
        //                 this.setGrant(grant);
        //                 res(this.grant);
        //             };

        //             this.getAccessToken({
        //                 code: code
        //             }).subscribe(
        //                 grant => done(grant),
        //                 _ => {
        //                     // Falha ao recuperar o token ( não existe ou expirou)
        //                     this.getAccessToken({
        //                         request_type: 'refreshToken',
        //                         refresh_token: this.grant.refresh_token
        //                     }).subscribe(
        //                         grant => done(grant),
        //                         error => res(error, false)
        //                     );
        //                 });
        //         },
        //         // Não conseguiu obter o codigo de autenticacao
        //         // acao tomada: redirecionar para a tela de login do sistema
        //         error => {
        //             // window.location.href = `).Raw("`${this.oauthURI}/signout`").Raw(`;
        //         }
        //     );
        // });