from typing import Iterable, List, Optional

from django.conf import settings

from staff.budget_position.models import BudgetPositionAssignmentStatus, ReplacementType
from staff.lib import xlsx

from staff.headcounts.headcounts_summary.assignments_page_model import RowModel, Name, Department


class AssignmentsSheetPresenter(xlsx.SheetPresenter):
    heading = (
        'Подразделение',
        'Иерархия подразделения',
        'Основной продукт\n (Value Stream)',
        'Иерархия основного продукта\n (Value Stream)',
        'География',
        'Категория',
        'Статус позиции',
        'Детали позиции',
        'ФИО',
        'Фемида',
        'Кому замена',
        'ФИО',
        'Численность',
        'Тип пересечения',
        'Есть замена',
        'ФИО',
        'ХК еще занят',
        'ФИО',
        'Номер БП',
        'Подразделение удалено',
    )

    _gray_row_format = {
        'font_color': '#D3D3D3',
    }

    _heading_format = {
        'align': 'center',
        'valign': 'vcenter',
        'text_wrap': True,
        'bold': True,
        'bg_color': '#FFCC00',
    }

    _without_outline_options = {'level': 0}
    _within_outline_options = {'level': 1, 'hidden': True}
    _collapsed_options = {'level': 0, 'collapsed': True}
    _model: Iterable[RowModel]

    def __init__(self, model: Iterable[RowModel], lang: str):
        super().__init__(model, 'Budget positions')
        self._lang = lang

    def _select_lang(self, name: Optional[Name]) -> str:
        if not name:
            return ''

        if self._lang == 'en':
            return name.name_en

        return name.name_ru

    def columns(self) -> Iterable[xlsx.Column]:
        return [
            xlsx.Column(width=25, options=self._without_outline_options),
            xlsx.Column(width=12, options=self._without_outline_options),
            xlsx.Column(width=25, options=self._without_outline_options),
            xlsx.Column(width=25, options=self._without_outline_options),
            xlsx.Column(width=19, options=self._without_outline_options),
            xlsx.Column(width=13, options=self._without_outline_options),
            xlsx.Column(width=19, options=self._without_outline_options),
            xlsx.Column(width=16, options=self._without_outline_options),
            xlsx.Column(width=16, options=self._without_outline_options),
            xlsx.Column(width=13, options=self._without_outline_options),
            xlsx.Column(width=13, options=self._collapsed_options),
            xlsx.Column(width=13, options=self._within_outline_options),
            xlsx.Column(width=13, options=self._without_outline_options),
            xlsx.Column(width=22, options=self._without_outline_options),
            xlsx.Column(width=22, options=self._collapsed_options),
            xlsx.Column(width=22, options=self._within_outline_options),
            xlsx.Column(width=16, options=self._collapsed_options),
            xlsx.Column(width=16, options=self._within_outline_options),
            xlsx.Column(width=17, options=self._without_outline_options),
            xlsx.Column(width=15, options=self._without_outline_options),
        ]

    def column_caption_row(self):
        return xlsx.ViewRow(
            xlsx.ViewCell(caption, self._heading_format)
            for caption in self.heading
        )

    def rows(self) -> Iterable[xlsx.ViewRow]:
        yield self.column_caption_row()

        for row in self._model:
            yield xlsx.ViewRow(
                cells=self.data_row_to_view_cells(row),
                row_format=self._row_format(row)
            )

    def _row_format(self, row: RowModel):
        return None if row.department.intranet_status else self._gray_row_format

    def _display_chain(self, chain: List[Department]):
        return ' → '.join(self._select_lang(dep.name) for dep in chain)

    def data_row_to_view_cells(self, data_row: RowModel) -> Iterable[xlsx.ViewCell]:
        value_stream_name = self._select_lang(data_row.value_stream.name)
        value_stream_chain = self._display_chain(data_row.value_stream_chain)
        geography = self._select_lang(data_row.geography.name)
        is_deleted = 'Нет' if data_row.department.intranet_status else 'Да'

        return (
            xlsx.ViewCell(text=self._select_lang(data_row.department.name)),
            xlsx.ViewCell(text=self._display_chain(data_row.department_chain)),
            xlsx.ViewCell(text=value_stream_name),
            xlsx.ViewCell(text=value_stream_chain),
            xlsx.ViewCell(text=geography),
            xlsx.ViewCell(text=data_row.category or ''),
            xlsx.ViewCell(text=AssignmentsSheetPresenter._get_text_for_status_column(data_row.status)),
            AssignmentsSheetPresenter._get_position_name_cell(data_row),
            xlsx.ViewCell(text=self._get_position_full_name(data_row)),
            AssignmentsSheetPresenter._get_vacancy_cell(data_row)
            or AssignmentsSheetPresenter._get_offer_cell(data_row)
            or xlsx.ViewCell(text=''),
            xlsx.ViewCell(text=self._get_prev_user_login(data_row)),
            xlsx.ViewCell(text=self._get_prev_user_fullname(data_row)),
            xlsx.ViewCell(text=str(data_row.headcount)),
            xlsx.ViewCell(text=AssignmentsSheetPresenter._get_replacement_type(data_row.replacement_type)),
            AssignmentsSheetPresenter._get_next_position_name(data_row),
            xlsx.ViewCell(text=self._get_next_position_full_name(data_row)),
            AssignmentsSheetPresenter._get_prev_position_name(data_row),
            xlsx.ViewCell(text=self._get_prev_position_full_name(data_row)),
            xlsx.ViewCell(text=str(data_row.code)),
            xlsx.ViewCell(text=is_deleted),
        )

    def _get_prev_user_login(self, row: RowModel) -> str:
        if not row.prev_budget_position_assignment:
            return ''
        return row.prev_budget_position_assignment.person_login or ''

    def _get_prev_user_fullname(self, row: RowModel) -> str:
        if not row.prev_budget_position_assignment:
            return ''

        first_name = self._select_lang(row.prev_budget_position_assignment.person_first_name)
        last_name = self._select_lang(row.prev_budget_position_assignment.person_last_name)
        return f'{first_name} {last_name}'

    def _get_position_full_name(self, row: Optional[RowModel]) -> str:
        if not row:
            return 'Нет доступа'
        if not row.person_login:
            return ''

        return f'{self._select_lang(row.person_first_name)} {self._select_lang(row.person_last_name)}'

    @staticmethod
    def _get_position_name_cell(row: Optional[RowModel]) -> xlsx.ViewCell:
        if not row:
            return xlsx.ViewCell(text='Нет доступа')
        if row.person_login:
            return xlsx.ViewCell(text=row.person_login)
        return AssignmentsSheetPresenter._get_ticket_cell(row) or xlsx.ViewCell(text=row.name or '')

    @staticmethod
    def _get_vacancy_cell(row: RowModel) -> Optional[xlsx.ViewCell]:
        if row.status == BudgetPositionAssignmentStatus.VACANCY_OPEN and row.vacancy:
            return xlsx.ViewCell(
                text=row.vacancy.name,
                url='https://{}/vacancies/{}/'.format(settings.FEMIDA_HOST, row.vacancy.id),
            )

        return None

    @staticmethod
    def _get_offer_cell(row: RowModel) -> Optional[xlsx.ViewCell]:
        if row.status == BudgetPositionAssignmentStatus.OFFER and row.vacancy:
            return xlsx.ViewCell(
                text=f'{row.vacancy.candidate_first_name} {row.vacancy.candidate_last_name}',
                url=f'https://{settings.FEMIDA_HOST}/candidates/{row.vacancy.candidate_id}/',
            )
        return None

    @staticmethod
    def _get_ticket_cell(row: RowModel) -> Optional[xlsx.ViewCell]:
        available_statuses = (
            BudgetPositionAssignmentStatus.VACANCY_OPEN,
            BudgetPositionAssignmentStatus.OFFER,
        )
        if row.status in available_statuses and row.vacancy and row.vacancy.ticket:
            return xlsx.ViewCell(
                text=row.vacancy.ticket,
                url='{}/{}'.format(settings.STARTREK_URL, row.vacancy.ticket),
            )
        return None

    @staticmethod
    def _get_next_position_name(row: RowModel) -> xlsx.ViewCell:
        available_types = (ReplacementType.HAS_REPLACEMENT, ReplacementType.HAS_REPLACEMENT_AND_BUSY)
        if row.replacement_type in available_types:
            return AssignmentsSheetPresenter._get_position_name_cell(row.next_budget_position_assignment)
        return xlsx.ViewCell(text='')

    @staticmethod
    def _get_prev_position_name(row: RowModel) -> xlsx.ViewCell:
        available_types = [ReplacementType.BUSY, ReplacementType.HAS_REPLACEMENT_AND_BUSY]
        if row.replacement_type in available_types:
            return AssignmentsSheetPresenter._get_position_name_cell(row.prev_budget_position_assignment)
        return xlsx.ViewCell(text='')

    def _get_next_position_full_name(self, row) -> str:
        available_types = [ReplacementType.HAS_REPLACEMENT, ReplacementType.HAS_REPLACEMENT_AND_BUSY]
        if row.replacement_type in available_types:
            return self._get_position_full_name(row.next_budget_position_assignment)
        return ''

    def _get_prev_position_full_name(self, row) -> str:
        available_types = [ReplacementType.BUSY, ReplacementType.HAS_REPLACEMENT_AND_BUSY]
        if row.replacement_type in available_types:
            return self._get_position_full_name(row.prev_budget_position_assignment)
        return ''

    @staticmethod
    def _get_text_for_status_column(value):
        statuses_map = {
            BudgetPositionAssignmentStatus.OCCUPIED: 'Работает',
            BudgetPositionAssignmentStatus.OFFER: 'Оффер',
            BudgetPositionAssignmentStatus.VACANCY_OPEN: 'Открытая вакансия',
            BudgetPositionAssignmentStatus.VACANCY_PLAN: 'Незанятый хедкаунт',
            BudgetPositionAssignmentStatus.RESERVE: 'Кредит',
            BudgetPositionAssignmentStatus.MATERNITY: 'Декрет',
        }

        return statuses_map.get(value, value)

    @staticmethod
    def _get_replacement_type(replacement_type: ReplacementType) -> str:
        types = {
            ReplacementType.HAS_REPLACEMENT: 'Есть замена',
            ReplacementType.BUSY: 'ХК еще занят',
            ReplacementType.HAS_REPLACEMENT_AND_BUSY: 'Есть замена и ХК еще занят',
            ReplacementType.WITHOUT_REPLACEMENT: 'Нет пересечений',
        }

        return types.get(replacement_type, replacement_type.value)
