# -*- coding: utf-8 -*-

# Copyright (C) 2013-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/


"""
my account controller for RhodeCode admin
"""

import logging

import formencode
from formencode import htmlfill
from pyramid.httpexceptions import HTTPFound

from pylons import request, tmpl_context as c, url
from pylons.controllers.util import redirect
from pylons.i18n.translation import _
from sqlalchemy.orm import joinedload

from rhodecode.lib import helpers as h
from rhodecode.lib import auth
from rhodecode.lib.auth import (
    LoginRequired, NotAnonymous, AuthUser)
from rhodecode.lib.base import BaseController, render
from rhodecode.lib.utils import jsonify
from rhodecode.lib.utils2 import safe_int, str2bool
from rhodecode.lib.ext_json import json

from rhodecode.model.db import (
    Repository, PullRequest, UserEmailMap, User, UserFollowing)
from rhodecode.model.forms import UserForm
from rhodecode.model.scm import RepoList
from rhodecode.model.user import UserModel
from rhodecode.model.repo import RepoModel
from rhodecode.model.meta import Session
from rhodecode.model.pull_request import PullRequestModel
from rhodecode.model.comment import CommentsModel

log = logging.getLogger(__name__)


class MyAccountController(BaseController):
    """REST Controller styled on the Atom Publishing Protocol"""
    # To properly map this controller, ensure your config/routing.py
    # file has a resource setup:
    #     map.resource('setting', 'settings', controller='admin/settings',
    #         path_prefix='/admin', name_prefix='admin_')

    @LoginRequired()
    @NotAnonymous()
    def __before__(self):
        super(MyAccountController, self).__before__()

    def __load_data(self):
        c.user = User.get(c.rhodecode_user.user_id)
        if c.user.username == User.DEFAULT_USER:
            h.flash(_("You can't edit this user since it's"
                      " crucial for entire application"), category='warning')
            return redirect(h.route_path('users'))

        c.auth_user = AuthUser(
            user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)

    def _load_my_repos_data(self, watched=False):
        if watched:
            admin = False
            follows_repos = Session().query(UserFollowing)\
                .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
                .options(joinedload(UserFollowing.follows_repository))\
                .all()
            repo_list = [x.follows_repository for x in follows_repos]
        else:
            admin = True
            repo_list = Repository.get_all_repos(
                user_id=c.rhodecode_user.user_id)
            repo_list = RepoList(repo_list, perm_set=[
                'repository.read', 'repository.write', 'repository.admin'])

        repos_data = RepoModel().get_repos_as_dict(
            repo_list=repo_list, admin=admin)
        # json used to render the grid
        return json.dumps(repos_data)

    @auth.CSRFRequired()
    def my_account_update(self):
        """
        POST /_admin/my_account Updates info of my account
        """
        # url('my_account')
        c.active = 'profile_edit'
        self.__load_data()
        c.perm_user = c.auth_user
        c.extern_type = c.user.extern_type
        c.extern_name = c.user.extern_name

        defaults = c.user.get_dict()
        update = False
        _form = UserForm(edit=True,
                         old_data={'user_id': c.rhodecode_user.user_id,
                                   'email': c.rhodecode_user.email})()
        form_result = {}
        try:
            post_data = dict(request.POST)
            post_data['new_password'] = ''
            post_data['password_confirmation'] = ''
            form_result = _form.to_python(post_data)
            # skip updating those attrs for my account
            skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
                          'new_password', 'password_confirmation']
            # TODO: plugin should define if username can be updated
            if c.extern_type != "rhodecode":
                # forbid updating username for external accounts
                skip_attrs.append('username')

            UserModel().update_user(
                c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
            h.flash(_('Your account was updated successfully'),
                    category='success')
            Session().commit()
            update = True

        except formencode.Invalid as errors:
            return htmlfill.render(
                render('admin/my_account/my_account.mako'),
                defaults=errors.value,
                errors=errors.error_dict or {},
                prefix_error=False,
                encoding="UTF-8",
                force_defaults=False)
        except Exception:
            log.exception("Exception updating user")
            h.flash(_('Error occurred during update of user %s')
                    % form_result.get('username'), category='error')

        if update:
            raise HTTPFound(h.route_path('my_account_profile'))

        return htmlfill.render(
            render('admin/my_account/my_account.mako'),
            defaults=defaults,
            encoding="UTF-8",
            force_defaults=False
        )

    def my_account_edit(self):
        """
        GET /_admin/my_account/edit Displays edit form of my account
        """
        c.active = 'profile_edit'
        self.__load_data()
        c.perm_user = c.auth_user
        c.extern_type = c.user.extern_type
        c.extern_name = c.user.extern_name

        defaults = c.user.get_dict()
        return htmlfill.render(
            render('admin/my_account/my_account.mako'),
            defaults=defaults,
            encoding="UTF-8",
            force_defaults=False
        )

    def my_account_repos(self):
        c.active = 'repos'
        self.__load_data()

        # json used to render the grid
        c.data = self._load_my_repos_data()
        return render('admin/my_account/my_account.mako')

    def my_account_watched(self):
        c.active = 'watched'
        self.__load_data()

        # json used to render the grid
        c.data = self._load_my_repos_data(watched=True)
        return render('admin/my_account/my_account.mako')

    def my_account_perms(self):
        c.active = 'perms'
        self.__load_data()
        c.perm_user = c.auth_user

        return render('admin/my_account/my_account.mako')

    def _extract_ordering(self, request):
        column_index = safe_int(request.GET.get('order[0][column]'))
        order_dir = request.GET.get('order[0][dir]', 'desc')
        order_by = request.GET.get(
            'columns[%s][data][sort]' % column_index, 'name_raw')
        return order_by, order_dir

    def _get_pull_requests_list(self, statuses):
        start = safe_int(request.GET.get('start'), 0)
        length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
        order_by, order_dir = self._extract_ordering(request)

        pull_requests = PullRequestModel().get_im_participating_in(
            user_id=c.rhodecode_user.user_id,
            statuses=statuses,
            offset=start, length=length, order_by=order_by,
            order_dir=order_dir)

        pull_requests_total_count = PullRequestModel().count_im_participating_in(
            user_id=c.rhodecode_user.user_id, statuses=statuses)

        from rhodecode.lib.utils import PartialRenderer
        _render = PartialRenderer('data_table/_dt_elements.mako')
        data = []
        for pr in pull_requests:
            repo_id = pr.target_repo_id
            comments = CommentsModel().get_all_comments(
                repo_id, pull_request=pr)
            owned = pr.user_id == c.rhodecode_user.user_id
            status = pr.calculated_review_status()

            data.append({
                'target_repo': _render('pullrequest_target_repo',
                                       pr.target_repo.repo_name),
                'name': _render('pullrequest_name',
                                pr.pull_request_id, pr.target_repo.repo_name,
                                short=True),
                'name_raw': pr.pull_request_id,
                'status': _render('pullrequest_status', status),
                'title': _render(
                    'pullrequest_title', pr.title, pr.description),
                'description': h.escape(pr.description),
                'updated_on': _render('pullrequest_updated_on',
                                      h.datetime_to_time(pr.updated_on)),
                'updated_on_raw': h.datetime_to_time(pr.updated_on),
                'created_on': _render('pullrequest_updated_on',
                                      h.datetime_to_time(pr.created_on)),
                'created_on_raw': h.datetime_to_time(pr.created_on),
                'author': _render('pullrequest_author',
                                  pr.author.full_contact, ),
                'author_raw': pr.author.full_name,
                'comments': _render('pullrequest_comments', len(comments)),
                'comments_raw': len(comments),
                'closed': pr.is_closed(),
                'owned': owned
            })
        # json used to render the grid
        data = ({
            'data': data,
            'recordsTotal': pull_requests_total_count,
            'recordsFiltered': pull_requests_total_count,
        })
        return data

    def my_account_pullrequests(self):
        c.active = 'pullrequests'
        self.__load_data()
        c.show_closed = str2bool(request.GET.get('pr_show_closed'))

        statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
        if c.show_closed:
            statuses += [PullRequest.STATUS_CLOSED]
        data = self._get_pull_requests_list(statuses)
        if not request.is_xhr:
            c.data_participate = json.dumps(data['data'])
            c.records_total_participate = data['recordsTotal']
            return render('admin/my_account/my_account.mako')
        else:
            return json.dumps(data)

    def my_notifications(self):
        c.active = 'notifications'
        return render('admin/my_account/my_account.mako')

    @auth.CSRFRequired()
    @jsonify
    def my_notifications_toggle_visibility(self):
        user = c.rhodecode_user.get_instance()
        new_status = not user.user_data.get('notification_status', True)
        user.update_userdata(notification_status=new_status)
        Session().commit()
        return user.user_data['notification_status']