import os
import openpyxl
import pickle
import numbers
import csv
from datetime import datetime
import json
from PyQt5.QtWidgets import QMainWindow, QToolBar, QFileDialog, QMessageBox, QListWidgetItem, QProgressDialog
from PyQt5.QtCore import Qt
from roomdata_window import Ui_MainWindow
from add_rule_window_main import AddRuleMainWindow


class RoomdataMainWindow(QMainWindow):
    def __init__(self):
        super(RoomdataMainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.add_toolbar()
        self.bind_actions()
        self.init_vars()

    def add_toolbar(self):
        self.toolbar = QToolBar()
        self.addToolBar(Qt.TopToolBarArea, self.toolbar)
        self.toolbar.addAction(self.ui.actionopen_file)
        self.toolbar.addAction(self.ui.actionopen_dir)

    def bind_actions(self):
        self.ui.actionopen_dir.triggered.connect(self.open_dir)
        self.ui.actionopen_file.triggered.connect(self.open_file)
        self.ui.add_rule_btn.clicked.connect(self.add_rule)
        self.ui.remove_rule_btn.clicked.connect(self.remove_rule)
        self.ui.edit_rule_btn.clicked.connect(self.edit_rule)
        self.ui.open_rule_btn.clicked.connect(self.open_rule)
        self.ui.save_rule_btn.clicked.connect(self.save_rule)
        self.ui.rules_widget.itemClicked.connect(self.rule_item_clicked)
        self.ui.start_process_btn.clicked.connect(self.start_process)

    def init_vars(self):
        self.opened_files = list()
        self.table_header = list()
        self.rule = list()
        self.ui.start_process_btn.setEnabled(False)
        self.ui.time_combo_box.addItems([f'{i}月' for i in range(1, 13)])
        self.ui.time_combo_box.setCurrentIndex(0)

    def open_dir(self, checked):
        file_dialog = QFileDialog(self)
        file_dialog.setFileMode(QFileDialog.DirectoryOnly)
        dir_name = file_dialog.getExistingDirectory()
        file_names = os.listdir(dir_name)
        for file_name in file_names:
            if not file_name.endswith('xlsx'):
                QMessageBox.critical(self, '错误', '文件夹中包含非表格文件', QMessageBox.Yes)
                return
        file_names = [os.path.join(dir_name, item) for item in file_names]
        self.opened_files = file_names
        self.show_opened_files()


    def open_file(self, checked):
        file_dialog = QFileDialog(self)
        file_dialog.setFileMode(QFileDialog.ExistingFiles)
        file_names = file_dialog.getOpenFileNames(filter='表格文件(*.xlsx)')[0]
        if file_names and len(file_names) > 0:
            self.opened_files = file_names
            self.show_opened_files()
        else:
            QMessageBox.critical(self, '错误', '请选择正确文件', QMessageBox.Yes)

    def check_file_headers(self):
        if not self.opened_files:
            return True, []
        wb = openpyxl.load_workbook(self.opened_files[0])
        sheet = wb['Sheet1']
        num_columns = sheet.max_column
        header = [sheet.cell(1, i).value.strip() for i in range(1, num_columns + 1)]
        for i in range(1, len(self.opened_files)):
            wb = openpyxl.load_workbook(self.opened_files[i])
            sheet = wb['Sheet1']
            if sheet.max_column != num_columns:
                return False, []
            for col in range(1, num_columns + 1):
                val = sheet.cell(1, col).value.strip()
                if val != header[col - 1]:
                    return False, []
        return True, header

    def show_opened_files(self):
        ret, header = self.check_file_headers()
        if ret and header:
            self.ui.opened_files_widget.clear()
            for file_name in self.opened_files:
                show_name = os.path.basename(file_name)
                self.ui.opened_files_widget.addItem(show_name)
            self.table_header = header
            self.ui.excel_header_widget.clear()
            for item in header:
                self.ui.excel_header_widget.addItem(item)
        else:
            self.opened_files.clear()
            self.ui.opened_files_widget.clear()
            QMessageBox.critical(self, '错误', '选择文件的表格头不一致', QMessageBox.Yes)

    def add_rule(self):
        if self.opened_files:
            self.add_rule_window = AddRuleMainWindow(self.table_header)
            self.add_rule_window.setWindowModality(Qt.ApplicationModal)
            ret = self.add_rule_window.exec_()
            if ret:
                name, indexes = self.add_rule_window.get_result()
                indexes = [item.row() for item in indexes]
                if not name or not indexes:
                    QMessageBox.critical(self, '错误', '请输入规则名并选择对应项', QMessageBox.Yes)
                else:
                    self.rule.append([name, [(i, self.table_header[i]) for i in indexes]])
                    self.refresh_rule_widget()
        else:
            QMessageBox.critical(self, '错误', '请先打开文件', QMessageBox.Yes)

    def remove_rule(self):
        selected_index = self.ui.rules_widget.selectedIndexes()
        if selected_index:
            selected_index = selected_index[0].row()
            self.ui.rule_detail_widget.clear()
            self.rule.pop(selected_index)
            self.refresh_rule_widget()

    def edit_rule(self):
        selected_index = self.ui.rules_widget.selectedIndexes()
        if selected_index:
            selected_index = selected_index[0].row()
            rule = self.rule[selected_index]
            self.add_rule_window = AddRuleMainWindow(self.table_header, rule[0], [rule[1][i][0] for i in range(len(rule[1]))])
            self.add_rule_window.setWindowModality(Qt.ApplicationModal)
            ret = self.add_rule_window.exec_()
            if ret:
                name, indexes = self.add_rule_window.get_result()
                indexes = [item.row() for item in indexes]
                try:
                    self.rule[selected_index][0] = name
                    self.rule[selected_index][1] = [(i, self.table_header[i]) for i in indexes]
                    self.refresh_rule_widget()
                except Exception as e:
                    print(e)


    def open_rule(self):
        if not self.opened_files:
            QMessageBox.critical(self, '错误', '请首先打开文件', QMessageBox.Yes)
        else:
            file_dialog = QFileDialog(self)
            file_dialog.setFileMode(QFileDialog.ExistingFile)
            file_name, _ = file_dialog.getOpenFileName(filter='规则(*.pkl)')
            if file_name:
                ret = self.open_and_check_rule_file(file_name)
                if not ret:
                    QMessageBox.critical(self, '错误', '所选文件不是规则文件或与所打开表格头不一致', QMessageBox.Yes)
                else:
                    self.refresh_rule_widget()

    def open_and_check_rule_file(self, file_path):
        with open(file_path, 'rb') as f:
            data = pickle.load(f)
        if not isinstance(data, list):
            return False
        for item in data:
            if not isinstance(item, list) or len(item) != 2 or not isinstance(item[0], str) or not isinstance(item[1], list):
                return False
            for it in item[1]:
                if not isinstance(it, tuple) or not isinstance(it[0], numbers.Number) or it[1] not in self.table_header:
                    return False
        self.rule = data
        return True

    def save_rule(self):
        if not self.rule:
            QMessageBox.critical(self, '错误', '请首先创建规则', QMessageBox.Yes)
        else:
            file_path, _ = QFileDialog.getSaveFileName(self, '保存规则', './', '规则文件(*.pkl)')
            if file_path:
                with open(file_path, 'wb') as f:
                    pickle.dump(self.rule, f)

    def refresh_rule_widget(self):
        self.ui.rule_detail_widget.clear()
        self.ui.rules_widget.clear()
        for rule in self.rule:
            self.ui.rules_widget.addItem(rule[0])
        if self.rule:
            self.ui.start_process_btn.setEnabled(True)

    def rule_item_clicked(self, item: QListWidgetItem):
        selected_index = self.ui.rules_widget.selectedIndexes()[0].row()
        data = self.rule[selected_index][1]
        self.ui.rule_detail_widget.clear()
        for item in data:
            self.ui.rule_detail_widget.addItem(item[1])

    def start_process(self):
        csv_result = self.generate_csv()
        self.generate_score(csv_result)

    def generate_score(self, csv_result):
        total_num = [len(c) * len(self.rule) for c in csv_result]
        total_num = sum(total_num)
        count = 0
        progress = QProgressDialog(self)
        progress.setWindowTitle("正在处理")
        progress.setLabelText("正在生成结果")
        progress.setMinimumDuration(1)
        progress.setWindowModality(Qt.WindowModal)
        progress.setRange(0, total_num)
        dst_path = os.path.dirname(self.opened_files[0])
        for index, file_name in enumerate(self.opened_files):
            data = csv_result[index]
            result = {}
            for item in data:
                address = item['address']
                result[address] = {}
                for key, value in self.rule:
                    score_dict = {'满意': 0, '较满意': 0, '一般': 0, '不满意': 0, '很不满意': 0, '未接触': 0}
                    for _, name in value:
                        try:
                            d = item[name]
                        except KeyError as e:
                            print(file_name, name)
                        d = d.replace("'", "\"")
                        d = json.loads(d)
                        result[address]['样本量'] = sum(d.values())
                        for k, v in d.items():
                            if k in score_dict:
                                score_dict[k] += v
                    try:
                        score = 100 * (score_dict['满意'] + score_dict['较满意'] * 0.9 + score_dict['一般'] * 0.7 +
                                       score_dict['不满意'] * 0.4) / (
                                            score_dict['满意'] + score_dict['较满意'] + score_dict['一般'] + score_dict[
                                        '不满意'] + score_dict['很不满意'])
                    except ZeroDivisionError as e:
                        score = 0
                    result[address][key] = score
                    count += 1
                    progress.setValue(count)
            header = ['address'] + [item[0] for item in self.rule] + ['样本量']
            base_file_name = os.path.basename(self.opened_files[index])
            with open(os.path.join(dst_path, f"{base_file_name.split('.')[0]}_result.csv"), 'w', encoding='utf',
                      newline='\n') as f:
                writer = csv.DictWriter(f, fieldnames=header)
                writer.writeheader()
                for address, v in result.items():
                    v['address'] = address
                    writer.writerow(v)

    def generate_csv(self):
        dst_path = os.path.dirname(self.opened_files[0])
        sheets = [openpyxl.load_workbook(name)['Sheet1'] for name in self.opened_files]
        total_num = 0
        for sheet in sheets:
            total_num += (sheet.max_row - 1) * (len(self.table_header) - 9)
        progress = QProgressDialog(self)
        progress.setWindowTitle("正在处理")
        progress.setLabelText("正在生成csv")
        progress.setMinimumDuration(1)
        progress.setWindowModality(Qt.WindowModal)
        progress.setRange(0, total_num)
        count = 0
        final_result = []
        for index, sheet in enumerate(sheets):
            num_row = sheet.max_row
            num_column = sheet.max_column
            column_names = ['address']
            for col in range(10, num_column + 1):
                column_names.append(sheet.cell(1, col).value.strip())
            result = {}
            for row in range(2, num_row + 1):
                submit_date = sheet.cell(row, 2).value.strip()
                submit_date = datetime.strptime(submit_date, '%Y/%m/%d %H:%M:%S')
                selected_month = self.ui.time_combo_box.currentIndex() + 1
                if submit_date.month != selected_month:
                    count += (len(self.table_header) - 9)
                    progress.setValue(count)
                    continue
                address = sheet.cell(row, 9).value.strip()
                if address not in result:
                    result[address] = {}
                for col in range(10, num_column + 1):
                    value = sheet.cell(row, col).value.strip()
                    if col not in result[address]:
                        result[address][col] = {'满意': 0, '较满意': 0, '一般': 0, '不满意': 0, '很不满意': 0,
                                                '未接触': 0}
                    if value in result[address][col].keys():
                        result[address][col][value] += 1
                    count += 1
                    progress.setValue(count)
            csv_result = []
            file_name = os.path.basename(self.opened_files[index])
            with open(os.path.join(dst_path, file_name.split('.')[0] + '.csv'), 'w', encoding='utf8') as f:
                writer = csv.DictWriter(f, column_names, lineterminator='\n')
                writer.writeheader()
                for address, data in result.items():
                    write_data = {}
                    write_data['address'] = address
                    for col in range(10, num_column + 1):
                        write_data[column_names[col - 9]] = str(result[address][col])
                    writer.writerow(write_data)
                    csv_result.append(write_data)
            final_result.append(csv_result)
        return final_result