package ru.yandex.webmaster.common.tvm;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.passport.tvmauth.BlackboxEnv;
import ru.yandex.passport.tvmauth.CheckedServiceTicket;
import ru.yandex.passport.tvmauth.CheckedUserTicket;
import ru.yandex.passport.tvmauth.deprecated.ServiceContext;
import ru.yandex.passport.tvmauth.deprecated.UserContext;


/**
 * Created by aleksart on 26.06.17.
 */
public class TVM2AuthService extends TVMAuthService {

    private static final Logger log = LoggerFactory.getLogger(TVM2AuthService.class);

    private ServiceContext serviceContext;
    private ServiceContext legacyServiceContext;
    private UserContext userContext;

    private TVM2RefreshKeyService tvm2RefreshKeyService;

    private int clientId;
    private Integer legacyClientId;
    private String secret;
    private BlackboxEnv blackboxEnv;

    public void init() {
        String tvmKeys = tvm2RefreshKeyService.getTvmKeys();
        serviceContext = new ServiceContext(clientId, secret, tvmKeys);
        if (legacyClientId != null) {
            legacyServiceContext = new ServiceContext(legacyClientId, secret, tvmKeys);
        }
        userContext = new UserContext(blackboxEnv, tvmKeys);
        ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
        updateKeys();
        timer.scheduleAtFixedRate(this::updateKeys, 1, 1, TimeUnit.HOURS);
    }

    private void updateKeys() {
        try {
            String newKeys = tvm2RefreshKeyService.getTvmKeys();
            serviceContext.resetKeys(newKeys);
            userContext.resetKeys(newKeys);
            if (legacyServiceContext != null) {
                legacyServiceContext.resetKeys(newKeys);
            }
            log.info("Updated tvm keys");
        } catch (Exception e) {
            log.error("Updating tvm keys failed", e);
        }
    }

    @Override
    public TVMServiceTicket validateTicket(byte[] ticketBytes) {
        TVMServiceTicket result = validateWithContext(ticketBytes, serviceContext);
        if (result == null && legacyServiceContext != null) {
            result = validateWithContext(ticketBytes, legacyServiceContext);
            if (result != null) {
                log.warn("Client {} authorized with legacy client id {}", result.getClientId(), legacyClientId);
            }
        }
        return result;
    }

    private static TVMServiceTicket validateWithContext(byte[] ticketBytes, ServiceContext context) {
        CheckedServiceTicket ticket;
        try {
            ticket = context.check(new String(ticketBytes, StandardCharsets.US_ASCII));
        } catch (Exception e) {
            log.error("TVM ticket validation failed: {}", e.getMessage());
            return null;
        }
        if (ticket.booleanValue()) {
            return new TVMServiceTicket(ticket.getSrc(), ticket.debugInfo());
        } else {
            log.error("Invalid ticket: {}", ticket.getStatus().toString());
            return null;
        }

    }

    public TVMUserTicket validateUserTicket(byte[] ticketBytes) {
        CheckedUserTicket ticket;
        try {
            ticket = userContext.check(new String(ticketBytes, StandardCharsets.US_ASCII));
        } catch (Exception e) {
            log.info("TVM user ticket validation failed: {}", e.getMessage());
            return null;
        }
        if (ticket.booleanValue()) {
            return new TVMUserTicket(ticket.getDefaultUid());
        } else {
            log.error("Invalid user ticket: {}", ticket.debugInfo());
            return null;
        }
    }

    @Required
    public void setTvm2RefreshKeyService(TVM2RefreshKeyService tvm2RefreshKeyService) {
        this.tvm2RefreshKeyService = tvm2RefreshKeyService;
    }

    @Required
    public void setClientId(int clientId) {
        this.clientId = clientId;
    }

    public void setLegacyClientId(int legacyClientId) {
        if (legacyClientId > 0) {
            this.legacyClientId = legacyClientId;
        }
    }

    @Required
    public void setSecret(String secret) {
        this.secret = secret;
    }

    @Required
    public void setBlackboxEnv(BlackboxEnv blackboxEnv) {
        this.blackboxEnv = blackboxEnv;
    }
}
