package ru.yandex.wmtools.common.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;

import ru.yandex.common.framework.user.UserInfo;
import ru.yandex.wmtools.common.data.info.WMUserInfo;
import ru.yandex.wmtools.common.error.ExtraTagInfo;
import ru.yandex.wmtools.common.error.ExtraTagNameEnum;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.error.UserProblem;

/**
 * Created by IntelliJ IDEA.
 * User: senin
 * Date: 20.03.2007
 * Time: 10:09:24
 */
abstract public class UserInfoService <T extends WMUserInfo> extends AbstractDbService implements IUserInfoService {
    private static final String USER_SERVICES_SERVICE_XPATH = "/user/services/service";
    private static final String ID_ATTR = "id";

    private UserService userService;
    protected UserService getUserService() {
        return userService;
    }
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    private ISupportService supportService;
    public ISupportService getSupportService() {
        return supportService;
    }
    public void setSupportService(ISupportService supportService) {
        this.supportService = supportService;
    }

    @Override
    final public T getUserInfo(long userId) throws InternalException, UserException {
        List<T> resultList = doUserInfoQuery(userId);

        if (resultList.size() != 1) {
            throw new UserException(UserProblem.NO_SUCH_USER_IN_SERVICE, UserProblem.NO_SUCH_USER_IN_SERVICE.toString() + ". UserId: " + userId);
        }
        return resultList.get(0);
    }

    abstract protected List<T> doUserInfoQuery(long userId) throws InternalException;

    @Override
    abstract public void createNewUser(long userId) throws UserException, InternalException;

    /**
     * Given user name or user uid, this method checks, if such user exists IN THIS SERVICE and returns
     * his user uid or throws exception.
     *
     * @param user User login or user uid.
     * @param paramName Parameter, user to get user id.
     * @return Returns user uid if such user exists, or <code>null</code> if it doesn't.
     * @throws InternalException If smth goes wrong.
     * @throws UserException If user has provided wrong data.
     */
    @Override
    public long getAndCheckUserIdByNameOrId(String user, String paramName) throws UserException, InternalException {
        long uid = getUserIdByNameOrId(user, paramName);

        if (!supportService.isKnownUser(uid)) {
            throw new UserException(UserProblem.NO_SUCH_USER_IN_SERVICE, "No such user in WMConsole: " + user);
        }

        return uid;
    }

    /**
     * Given user name or user uid, this method checks, if such user exists in Yandex.Passport and returns
     * his user uid or throws exception.
     *
     * @param user User login or user uid.
     * @param paramName Parameter, user to get user id.
     * @return Returns user uid if such user exists.
     * @throws UserException Thrown when no user was found or query for user was illegal.
     */
    @Override
    public long getUserIdByNameOrId(String user, String paramName) throws UserException {
        if ((user == null) || (user.length() == 0)) {
            throw new UserException(UserProblem.ILLEGAL_PARAM_VALUE, "Param '" + paramName + "' has null or empty value.", new ExtraTagInfo(ExtraTagNameEnum.PARAM, paramName), new ExtraTagInfo(ExtraTagNameEnum.VALUE, user));
        }

        if (user.matches("[0-9]*")) {
            // we are given UID
            try {
                return Long.parseLong(user);
            } catch (NumberFormatException e) {
                throw new UserException(UserProblem.ILLEGAL_VALUE_TYPE, "Param '" + paramName + "' starts from digit, but is not legal integer.", new ExtraTagInfo(ExtraTagNameEnum.PARAM, paramName), new ExtraTagInfo(ExtraTagNameEnum.VALUE, user));
            }
        } else {
            // we are given Login
            return getUidByLogin(user);
        }
    }

    private long getUidByLogin(String userLogin) throws UserException {
        Long userId = userService.getUidByLogin(userLogin);
        if (userId == null) {
            throw new UserException(UserProblem.NO_SUCH_USER_IN_PASSPORT, "No such user in Yandex.Passport. No such user corresponds to given login: " + userLogin);
        }
        return userId;
    }

    @Override
    public boolean isKnownUser(long userId) throws InternalException {
        List<T> resultList = doUserInfoQuery(userId);
        return !resultList.isEmpty();
    }

    public Boolean hasSID(long userId, int SID) throws InternalException {
//        try {
//            WMUserInfo userInfo = getUserInfo(userId);
//            if (userInfo == null) {
//                return false;
//            }
//            String allInfo = userInfo.getAuthor().getAllInfo();
//            SAXBuilder builder = new SAXBuilder();
//            Document info = builder.build(new StringReader(allInfo));
//            List sids = XPath.selectNodes(info, USER_SERVICES_SERVICE_XPATH);
//            for (Object sid : sids) {
//                if (sid instanceof Element) {
//                    if (((Element)sid).getAttribute(ID_ATTR).getIntValue() == SID) {
//                        return true;
//                    }
//                }
//            }
//        }
//        catch (Exception e) {
//            throw new InternalException(InternalProblem.PROCESSING_ERROR, "Cannot check SID " + SID + " for user " + userId + "!", e);
//        }
        return false;
    }

    final public UserInfo getPassportUserInfo(long userId) {
        return getUserService().getUserInfo(userId);
    }

    /**
     * Given a string with users (each user can be given by either user id, or user login),
     * delimited by commas and/or semicolons, and EMPTY sets of correct and incorrect users list,
     * it checks all users and returns user ids of all users, that are registered in current service in the first set
     * and all user ids (or user logins, what was given) that are not correct (not registered in Yandex.Webmaster or
     * even not found in Yandex.Passport) in the second set.
     *
     * @param userString            String of userString.
     * @param correctUsersList      Set of correct user ids.
     * @param incorrectUsersList    Set of incorrect user ids and logins.
     */
    @Override
    public void parseUserList(String userString, Collection<Long> correctUsersList, Collection<String> incorrectUsersList) {
        parseUserList(userString, correctUsersList, incorrectUsersList, false, new ArrayList<String>());
    }

    public void parseUserList(String userString, Collection<Long> correctUsersList, Collection<String> incorrectUsersList, boolean allowDirectEmails, Collection<String> directEmails) {
        correctUsersList.clear();
        incorrectUsersList.clear();
        directEmails.clear();

        StringTokenizer tok = new StringTokenizer(userString, ",;\r\n");
        while (tok.hasMoreTokens()) {
            String nextRecipient = tok.nextToken();

            if (nextRecipient.contains("@")) {
                // direct email provided
                if (allowDirectEmails) {
                    directEmails.add(nextRecipient);
                } else {
                    incorrectUsersList.add(nextRecipient);
                }
            } else {
                // user provided
                long userId = 0;
                try {
                    userId = getAndCheckUserIdByNameOrId(nextRecipient, "");
                } catch (UserException ignored) {
                } catch (InternalException ignored) {
                }

                if (userId != 0) {
                    correctUsersList.add(userId);
                } else {
                    incorrectUsersList.add(nextRecipient);
                }
            }
        }
    }
}
