import os
import re

from PyQt5 import uic
from PyQt5.QtCore import Qt, QVariant
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QHeaderView

from vorta.utils import (get_asset, get_dict_from_list, nested_dict)

from vorta.views.tree_view import TreeModel

uifile = get_asset('UI/diffresult.ui')
DiffResultUI, DiffResultBase = uic.loadUiType(uifile)


class DiffResult(DiffResultBase, DiffResultUI):
    def __init__(self, fs_data, archive_newer, archive_older):
        super().__init__()
        self.setupUi(self)

        files_with_attributes, nested_file_list = parse_diff_lines(fs_data.split('\n'))
        model = DiffTree(files_with_attributes, nested_file_list)

        view = self.treeView
        view.setAlternatingRowColors(True)
        view.setUniformRowHeights(True)  # Allows for scrolling optimizations.
        view.setModel(model)
        header = view.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(0, QHeaderView.Stretch)

        self.archiveNameLabel_1.setText(f'{archive_newer.name}')
        self.archiveNameLabel_2.setText(f'{archive_older.name}')
        self.okButton.clicked.connect(self.accept)


def parse_diff_lines(diff_lines):
    files_with_attributes = []
    nested_file_list = nested_dict()

    def parse_line(line):
        if line:
            line_split = line.split()
        else:
            return 0, "", "", ""

        if line_split[0] in {'added', 'removed', 'changed'}:
            change_type = line_split[0]
            if line_split[1] in ['directory', 'link']:
                size = 0
                full_path = re.search(r'^\w+ \w+ +(.*)', line).group(1)
            else:
                significand = line_split[1]
                unit = line_split[2]
                size = calc_size(significand, unit)
                full_path = re.search(r'^\w+ +\S+ \w?B (.*)', line).group(1)
        else:
            size_change = re.search(r' *[\+-]?(\d+\.*\d*) (\w?B) +[\+-]?.+\w?B ', line)
            if size_change:
                significand = size_change.group(1)
                unit = size_change.group(2)
                size = calc_size(significand, unit)
                full_path_index = size_change.end(0)
            else:
                size = 0

            permission_change = re.search(r' *(\[.{24}\]) ', line)
            if permission_change:
                change_type = permission_change.group(1)
                full_path_index = permission_change.end(0)
            else:
                change_type = "modified"

            if size_change and permission_change:
                full_path_index = max(size_change.end(0), permission_change.end(0))
            full_path = line[full_path_index:]

        dir, name = os.path.split(full_path)

        # add to nested dict of folders to find nested dirs.
        d = get_dict_from_list(nested_file_list, dir.split('/'))
        if name not in d:
            d[name] = {}

        return size, change_type, name, dir

    for line in diff_lines:
        files_with_attributes.append(parse_line(line))

    return (files_with_attributes, nested_file_list)


def calc_size(significand, unit):
    if unit == 'B':
        return int(significand)
    elif unit == 'kB':
        return int(float(significand) * 10**3)
    elif unit == 'MB':
        return int(float(significand) * 10**6)
    elif unit == 'GB':
        return int(float(significand) * 10**9)
    elif unit == 'TB':
        return int(float(significand) * 10**12)


class DiffTree(TreeModel):
    def __init__(
        self,
        files_with_attributes,
        nested_file_list,
        parent=None,
    ):
        super().__init__(
            files_with_attributes, nested_file_list, parent=parent
        )

    def data(self, index, role):
        if not index.isValid():
            return None

        item = index.internalPointer()

        if role == Qt.ForegroundRole:
            if item.itemData[1] == 'removed':
                return QVariant(QColor(Qt.red))
            elif item.itemData[1] == 'added':
                return QVariant(QColor(Qt.green))
            elif item.itemData[1] == 'modified' or item.itemData[1].startswith('['):
                return QVariant(QColor(Qt.darkYellow))

        if role == Qt.DisplayRole:
            return item.data(index.column())
        else:
            return None

    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlags
        return Qt.ItemIsEnabled