package ru.yandex.webmaster3.admin.service;

import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

import com.datastax.driver.core.utils.UUIDs;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import ru.yandex.webmaster3.admin.security.service.AdminUsersService;
import ru.yandex.webmaster3.admin.security.service.RoleRemoveObserver;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.data.WebmasterUser;
import ru.yandex.webmaster3.core.host.verification.UserHostVerificationInfo;
import ru.yandex.webmaster3.core.host.verification.VerificationCausedBy;
import ru.yandex.webmaster3.core.host.verification.VerificationType;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.user.UserVerifiedHost;
import ru.yandex.webmaster3.storage.util.ydb.exception.WebmasterYdbException;
import ru.yandex.webmaster3.storage.admin.AdminUserInfo;
import ru.yandex.webmaster3.storage.admin.SelfVerificationRecord;
import ru.yandex.webmaster3.storage.admin.dao.SelfHostVerificationsYDao;
import ru.yandex.webmaster3.storage.admin.security.AccessActionEnum;
import ru.yandex.webmaster3.storage.admin.security.AccessObjectEnum;
import ru.yandex.webmaster3.storage.admin.security.Permission;
import ru.yandex.webmaster3.storage.admin.security.Role;
import ru.yandex.webmaster3.storage.user.dao.UserHostVerificationYDao;
import ru.yandex.webmaster3.storage.user.service.UserHostsService;
import ru.yandex.webmaster3.storage.verification.HostVerifierService;

/**
 * @author avhaliullin
 */
public class AdminUsersHostsService implements RoleRemoveObserver {
    private static final Logger log = LoggerFactory.getLogger(AdminUsersHostsService.class);

    private static final Permission SELF_VERIFICATION_PERMISSION = new Permission(AccessActionEnum.WRITE, AccessObjectEnum.HOST_SELF_VERIFICATION);

    @Autowired
    private SelfHostVerificationsYDao selfHostVerificationsYDao;
    @Autowired
    private AdminUsersService adminUsersService;
    @Autowired
    private HostVerifierService hostVerifierService;
    @Autowired
    private UserHostVerificationYDao userHostVerificationYDao;
    @Autowired
    private UserHostsService userHostsService;

    public void createSelfVerification(long adminUserId, WebmasterHostId hostId) {
        try {
            long externalUid = adminUsersService.getAssociatedExternalUserId(adminUserId);

            WebmasterUser user = new WebmasterUser(externalUid);
            UserVerifiedHost userVerifiedHost = userHostsService.getVerifiedHost(user, hostId);
            if (userVerifiedHost != null) {
                return;
            }

            selfHostVerificationsYDao.addVerificationRecord(new SelfVerificationRecord(adminUserId, externalUid, hostId, DateTime.now()));
            startVerification(externalUid, hostId, VerificationType.SELF);

        } catch (WebmasterYdbException e) {
            throw new WebmasterException("Unable to create self verification",
                    new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);
        }
    }

    @Override
    public void observeUserRolesChange(AdminUserInfo userInfo, Set<Role> newRoles) {
        boolean hasSelfVerificationPermission = false;
        for (Role role : newRoles) {
            if (role.getPermissions().contains(SELF_VERIFICATION_PERMISSION)) {
                hasSelfVerificationPermission = true;
                break;
            }
        }

        if (!hasSelfVerificationPermission) {
            log.info("Cleaning user's {} self verifications", userInfo.getAdminUser().getLogin());
            removeSelfVerifications(userInfo.getAdminUser().getUserId());
        }
    }

    public void startVerification(long userId, WebmasterHostId hostId, VerificationType verificationType)  {
        UUID recordId = UUIDs.timeBased();
        UserHostVerificationInfo verificationInfo = userHostVerificationYDao.getLatestRecord(userId, hostId);
        if (verificationInfo == null) {
            verificationInfo = UserHostVerificationInfo.createVerificationStartRecord(
                    recordId,
                    userId,
                    hostId,
                    verificationType,
                    ThreadLocalRandom.current().nextLong(),
                    VerificationCausedBy.INITIAL_VERIFICATION
            );
        } else {
            verificationInfo = verificationInfo.copyAsVerificationStartRecord(recordId, verificationType, VerificationCausedBy.INITIAL_VERIFICATION);
        }
        userHostVerificationYDao.addVerificationRecord(verificationInfo);
        hostVerifierService.verifyHost(userId, hostId, recordId);
    }

    private void removeSelfVerifications(long adminUserId) {
        try {
            List<SelfVerificationRecord> selfVerifications = selfHostVerificationsYDao.listSelfVerificationsForUser(adminUserId);
            if (!selfVerifications.isEmpty()) {
                for (SelfVerificationRecord record : selfVerifications) {
                    selfHostVerificationsYDao.deleteRecord(record.getHostId(), record.getUserId());
                    startVerification(record.getUserId(), record.getHostId(), VerificationType.SELF);
                }
            }
        } catch (WebmasterYdbException e) {
            throw new WebmasterException("Unable to remove user's " + adminUserId + " self verification records",
                    new WebmasterErrorResponse.YDBErrorResponse(getClass(), e), e);
        }
    }
}
