from itertools import chain
import json

from django.http import JsonResponse
from django.views.decorators.http import require_http_methods, require_GET
from django.views.decorators.csrf import ensure_csrf_cookie

from staff.lib.decorators import responding_json, paginated, Paginator
from staff.lib.forms.errors import sform_general_error

from staff.budget_position.workflow_service import WorkflowRegistryService
from staff.departments.models import Department, PERSON_POSITION_STATUS
from staff.departments.controllers.exceptions import BpConflict
from staff.departments.tree_lib import TreeBuilder
from staff.person.models import Staff

from staff.headcounts.forms import CreditManagementApplicationForm
from staff.headcounts.headcounts_credit_management import (
    CreateUseCase,
    GetUseCase,
    CreateCreditRepaymentRequest,
    CreateCreditRepaymentRequestRow,
    HeadcountsPermissions,
    Workflows,
    Repository,
    Startrek,
)
from staff.headcounts.positions_entity_info import PositionsEntityInfo
from staff.headcounts.positions_filter_context import PositionsFilterContext


@ensure_csrf_cookie
@require_http_methods(['GET', 'POST'])
def credit_management_application(request):
    author = request.user.get_profile()
    use_case = CreateUseCase(
        HeadcountsPermissions(author),
        Workflows(WorkflowRegistryService()),
        Repository(author),
        Startrek()
    )

    if request.method == 'GET':
        return JsonResponse(CreditManagementApplicationForm().as_dict(), status=200)

    form = CreditManagementApplicationForm(json.loads(request.body))

    if not form.is_valid():
        return JsonResponse(form.errors_as_dict(), status=400)

    credit_repayment_request = CreateCreditRepaymentRequest(
        comment=form.cleaned_data['comment'],
        rows=[
            CreateCreditRepaymentRequestRow(row['credit'].id, row['repayment'].id)
            for row in form.cleaned_data['budget_positions']
        ],
    )

    if not use_case.has_rights(credit_repayment_request):
        return JsonResponse({}, status=403)

    try:
        use_case.create(credit_repayment_request)
    except BpConflict as e:
        return JsonResponse(sform_general_error(err_code='bp_conflict_error', params={'meta': e.meta}), status=400)

    return JsonResponse({'application_id': use_case.application_id}, status=200)


@require_GET
@paginated
@responding_json
def application_list(request, paginator: Paginator):
    applications_qs = Repository(request.user.get_profile()).list()
    paginator.update_by_queryset(applications_qs)

    for application in paginator.result:
        application['ticket'] = application.pop('startrek_headcount_key')
        application['is_active'] = application.pop('is_active')
        application['author'] = PositionsEntityInfo.user_data(application, 'author')

    return paginator.as_dict()


@require_GET
@responding_json
def application(request, application_id):
    permissions = HeadcountsPermissions(request.user.get_profile())
    repository = Repository(request.user.get_profile())
    get_use_case = GetUseCase(application_id, repository, permissions)

    if not get_use_case.has_access():
        return {}, 403

    credit_repayment = get_use_case.get()
    positions_codes = []
    for row in credit_repayment.rows:
        positions_codes.append((
            row.credit_budget_position.code,
            row.repayment_budget_position.code,
        ))

    filter_context = PositionsFilterContext(
        exclude_reserve=False,
        codes=chain.from_iterable(positions_codes),
        position_statuses=[
            PERSON_POSITION_STATUS.VACANCY_OPEN,
            PERSON_POSITION_STATUS.VACANCY_PLAN,
            PERSON_POSITION_STATUS.RESERVE,
        ],
    )

    filler = PositionsEntityInfo(filter_context)
    code_to_position = {position['code']: position for position in filler.full_entities_query()}
    filler.fill_positions(list(code_to_position.values()))
    departments_ids = [position['department_id'] for position in code_to_position.values()]
    departments = TreeBuilder(filler).get_as_short_list(Department.objects.filter(id__in=departments_ids).values())

    author = Staff.objects.get(login=credit_repayment.author_login)

    return {
        'items': [{
            'credit': code_to_position.get(credit_code),       # позиция может пропасть или быть в другом статусе
            'repayment': code_to_position.get(repayment_code)  # TODO: надо будет показывать что-нибудь на фронте
        } for credit_code, repayment_code in positions_codes],
        'author': {
            'login': author.login,
            'name': f'{author.i_first_name} {author.i_last_name}',
        },
        'comment': credit_repayment.comment,
        'ticket': credit_repayment.ticket,
        'is_active': credit_repayment.is_active,
        'departments_chains': {
            department['id']: department['chain']
            for department in departments
        }
    }
