import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from openpyxl.utils import get_column_letter
from datetime import datetime, date
import re
import os
import sys
from docx import Document
from docx.shared import Inches, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml import parse_xml
from docx.oxml.ns import nsdecls

sys.stdout.reconfigure(encoding='utf-8')

class DataCleanerAndMergerT39:
    def __init__(self):
        # Technicians map based on T38 with complete coverage
        self.kythuat_map = {
            'trần dũng': 'Trần Dũng',
            'trần dung': 'Trần Dũng',
            'trần thanh dung': 'Trần Thanh Dũng',
            'trần thanh dũng': 'Trần Thanh Dũng',
            'nguyễn văn tuân': 'Nguyễn Văn Tuân',
            'nguyễn văn tuấn': 'Nguyễn Văn Tuấn',
            'nguyên hưu lợi': 'Nguyễn Hữu Lợi',
            'nguyên hữu lợi': 'Nguyễn Hữu Lợi',
            'nguyễn hưu lợi': 'Nguyễn Hữu Lợi',
            'nguyễn hữu lợi': 'Nguyễn Hữu Lợi',
            'nguyễn hưu lơi': 'Nguyễn Hữu Lợi',
            'nguyên hưu lơi': 'Nguyễn Hữu Lợi',
            'lợi': 'Nguyễn Hữu Lợi',
            'kt cắm tại nha máy làm': 'Nguyễn Hữu Lợi',
            'nông đưc trường': 'Nông Đức Trường',
            'nông đức trường': 'Nông Đức Trường',
            'đỗ quang thuỷ': 'Đỗ Quang Thủy',
            'đỗ quang thủy': 'Đỗ Quang Thủy',
            'đõ quang thuỷ': 'Đỗ Quang Thủy',
            'nguyễn văn tùng': 'Nguyễn Văn Tùng',
            'đoàn thanh nhân': 'Đoàn Thanh Nhân',
            'đoàn thang nhân': 'Đoàn Thanh Nhân',
            'dương tấn hiền': 'Dương Tấn Hiền',
            'dươg tấn hiền': 'Dương Tấn Hiền',
            'trịnh hữu hải': 'Trịnh Hữu Hải',
            'trinh hữu hải': 'Trịnh Hữu Hải',
            'hải': 'Trịnh Hữu Hải',
            'phan văn hiếu': 'Phan Văn Hiếu',
            'lê văn hoa': 'Lê Văn Hòa',
            'lê văn hòa': 'Lê Văn Hòa',
            'lê văn hoà': 'Lê Văn Hòa',
            'bùi văn tuấn': 'Bùi Văn Tuấn',
            'phạm thanh hoàn': 'Phạm Thanh Hoàn',
            'lê xuân tương': 'Lê Xuân Tương',
            'lê xuân tuong': 'Lê Xuân Tương',
            'lê xuan tương': 'Lê Xuân Tương',
            'hồ tiến sĩ': 'Hồ Tiến Sĩ',
            'hồ tiến sỹ': 'Hồ Tiến Sĩ',
            'sỹ chí': 'Sỹ Chí',
            'ngọc dương': 'Ngọc Dương',
            'cao ngọc dương': 'Ngọc Dương',
            'trần văn thưởng': 'Trần Văn Thưởng',
            'trần thưởng': 'Trần Văn Thưởng',
            'nguyễn văn hoan': 'Nguyễn Văn Hoan',
            'nguyễn văn haon': 'Nguyễn Văn Hoan',
            'hoàng phong': 'Bùi Hoàng Phong',
            'bùi hoàng phong': 'Bùi Hoàng Phong',
            'hữu nhựt': 'Hữu Nhựt',
            'trần hữu nhựt': 'Hữu Nhựt',
            'hữu nghị': 'Hữu Nghị',
            'xuân tương': 'Lê Xuân Tương',
            'thanh hoàn': 'Phạm Thanh Hoàn',
            'phú vĩnh': 'Nguyễn Phú Vĩnh',
            'nguyễn phú vĩnh': 'Nguyễn Phú Vĩnh',
            'văn lịnh': 'Văn Lịnh',
            'đông (sơn nước)': 'Đông (Sơn Nước)',
            'đông , thưởng': 'Đông , Trần Văn Thưởng',
            'thưởng': 'Trần Văn Thưởng',
            'trần văn hát': 'Trần Văn Hát',
            'nho xuân': 'Nho Xuân',
            'nguyến văn ân': 'Nguyễn Văn Ân',
            'văn khương': 'Văn Khương',
            'nguyễn trường hận': 'Nguyễn Trường Hận',
            'bảo': 'Bảo',
            'chinh': 'Chinh',
            'coi': 'Còi',
            'còi': 'Còi',
            'dương': 'Ngọc Dương',
            'hiên': 'Hiên',
            'quốc hiên': 'Quốc Hiên',
            'hoàn': 'Phạm Thanh Hoàn',
            'khoi': 'Võ Quốc Khởi',
            'khởi': 'Võ Quốc Khởi',
            'khoqir': 'Võ Quốc Khởi',
            'khơi': 'Võ Quốc Khởi',
            'khương duy long': 'Khương Duy Long',
            'lê văn như': 'Lê Văn Như',
            'lê đăng duyên': 'Lê Đăng Duyên',
            'lưu thanh nhật tảo': 'Lưu Thanh Nhật Tảo',
            'nguyễn như quyền': 'Nguyễn Như Quyền',
            'nguyễn hư quyền': 'Nguyễn Như Quyền',
            'nguyễn quốc khánh': 'Nguyễn Quốc Khánh',
            'nguyễn thái bảo': 'Nguyễn Thái Bảo',
            'nguyễn thị thu': 'Nguyễn Thị Thu',
            'phạm văn linh': 'Phạm Văn Linh',
            'phạm văn tùng': 'Phạm Văn Tùng',
            'trương công minh': 'Trương Công Minh',
            'truong công minh': 'Trương Công Minh',
            'trương văn nghĩa': 'Trương Văn Nghĩa',
            'trần văn tùng': 'Trần Văn Tùng',
            'đào hóa em': 'Đào Hóa Em',
            'huỳnh công nông': 'Huỳnh Công Nông',
            'công nông': 'Huỳnh Công Nông',
            'lâm văn nông': 'Lâm Văn Nông',
            'lâm văn nong': 'Lâm Văn Nông',
            'vũ duy hậu': 'Vũ Duy Hậu',
            'đại phước': 'Đại Phước',
            'phước': 'Đại Phước',
            'võ đại phước': 'Đại Phước',
            'đạt': 'Đạt',
            'vịnh': 'Vịnh',
            'giáp văn sáu': 'Giáp Văn Sáu',
            'sáu': 'Giáp Văn Sáu',
            'quách văn vũ': 'Quách Văn Vũ',
            'quach văn vũ': 'Quách Văn Vũ',
            'thái thành lưu': 'Thái Thành Lưu',
            'thái văn tiến': 'Thái Văn Tiến',
            'đăng hiệp': 'Đăng Hiệp',
            'hiệp': 'Đăng Hiệp',
            'nguyễn đăng hiệp': 'Đăng Hiệp',
            'duy tài': 'Nguyễn Duy Tài',
            'nguyễn duy tài': 'Nguyễn Duy Tài',
            'nguyễn minh thoại': 'Nguyễn Minh Thoại',
            'minh thoại': 'Nguyễn Minh Thoại',
            'hồ văn tròn em': 'Hồ Văn Tròn Em',
            'tròn em': 'Hồ Văn Tròn Em',
            'vay lương thuận': 'Vay Lương Thuận',
            'vay lượng thuận': 'Vay Lương Thuận',
            'nguyễn văn thuận': 'Nguyễn Văn Thuận',
            'hồ văn việt': 'Hồ Văn Việt',
            'lê văn thành': 'Lê Văn Thành',
            'lê văn thành-kt mới': 'Lê Văn Thành',
            'phan văn việt-kt mới': 'Phan Văn Việt',
            'phan văn việt': 'Phan Văn Việt',
            'việt': 'Phan Văn Việt',
            'bùi văn giang': 'Bùi Văn Giang',
            'quang khái chỉnh': 'Quang Khải',
            'quang khải': 'Quang Khải',
            'thanh tuyến': 'Bành Thanh Tuyến',
            'bành thanh tuyến': 'Bành Thanh Tuyến',
            'duy khánh': 'Hồ Duy Khánh',
            'hồ duy khánh': 'Hồ Duy Khánh',
            'khánh': 'Hồ Duy Khánh',
            'nguyễn văn năm': 'Nguyễn Văn Năm',
            'trương vũ linh': 'Trương Vũ Linh',
            'trinh bá quyền': 'Trịnh Bá Quyền',
            'trịnh bá quyền': 'Trịnh Bá Quyền',
            'lê văn hạnh': 'Lê Văn Hạnh',
            'phạm văn vọng': 'Phạm Văn Vọng',
            'phạm văn vong': 'Phạm Văn Vọng',
            'quang tân': 'Nguyễn Quang Tân',
            'nguyễn quang tân': 'Nguyễn Quang Tân',
            'lê thành': 'Lê Thành',
            'nguyễn ngọc thạch': 'Nguyễn Ngọc Thạch',
            'bùi văn chất': 'Bùi Văn Chất',
            'hoàn xuân': 'Hoàn Xuân',
            'lâm hồng thông': 'Lâm Hồng Thông',
            'trần văn tuấn': 'Trần Văn Tuấn',
            'lê': 'Lê',
            'nguyễn hoàng khang': 'Nguyễn Hoàng Khang',
            'nguyễn kim giang': 'Nguyễn Kim Giang',
            'nguyễn đình vinh': 'Nguyễn Đình Vinh',
            'nguyễn đinh vinh': 'Nguyễn Đình Vinh',
            'ngyên đinh vinh': 'Nguyễn Đình Vinh',
            'nguyễn văn ninh': 'Nguyễn Văn Ninh',
            'thiêm': 'Phạm Văn Thiêm',
            'phạm văn thiêm': 'Phạm Văn Thiêm',
            'triệu văn đông': 'Triệu Văn Đông',
            'trần văn lê': 'Trần Văn Lê',
            'trung': 'Trung',
            'ngô văn trung': 'Ngô Văn Trung',
            'nguyễn trọng phong': 'Nguyễn Trọng Phong',
            'trọng phong': 'Nguyễn Trọng Phong',
            'dương minh tường': 'Dương Minh Tường',
            'duy khương': 'Duy Khương',
            'đặng văn chiến': 'Đặng Văn Chiến',
            'chiến': 'Đặng Văn Chiến',
            'vũ': 'Quách Văn Vũ',
            'tuấn': 'Bùi Văn Tuấn',
            'lương': 'Lương',
            'lâm thành công': 'Lâm Thành Công',
            'nguyễn minh nhí': 'Nguyễn Minh Nhí',
            'phạm văn thạch': 'Phạm Văn Thạch',
            'bùi quốc qui': 'Bùi Quốc Quy',
            'bùi quốc quy': 'Bùi Quốc Quy',
            'nguyễn trọng nhân': 'Nguyễn Trọng Nhân',
            'trần văn kép': 'Trần Văn Kép',
            'trinh văn thằng': 'Trịnh Văn Thắng',
            'trịnh văn thắng': 'Trịnh Văn Thắng',
            'nguyễn đức thăng': 'Nguyễn Đức Thăng',
            'nguyễn đức thắng': 'Nguyễn Đức Thăng',
            'đức thăng': 'Nguyễn Đức Thăng',
            'đức thắng': 'Nguyễn Đức Thăng',
            'thăng': 'Nguyễn Đức Thăng',
            'tạo': 'Tạo',
            'thơm': 'Thơm',
            'đoàn': 'Đoàn',
            'thanh xuân': 'Thanh Xuân',
            'hoàng văn sang': 'Hoàng Văn Sang',
            'hoàn văn sang': 'Hoàng Văn Sang',
            'lê văn bụng': 'Lê Văn Bụng',
            'trần quốc khang': 'Trần Quốc Khang',
            'ngô trọng nhân': 'Ngô Trọng Nhân',
            'nông quốc toản': 'Nông Quốc Toản',
            'hùng': 'Hùng',
            'nguyễn minh khang': 'Nguyễn Minh Khang',
            'nguyễn hoàng giang': 'Nguyễn Hoàng Giang',
            'xuân': 'Nho Xuân',
            'đông': 'Đông',
            'phạm văn dũng': 'Phạm Văn Dũng',
            'phạm văn lâm': 'Phạm Văn Lâm',
            'trình': 'Trình',
            'đỗ trọng long': 'Đỗ Trọng Long',
            'độ trọng long': 'Đỗ Trọng Long',
            'nguyễn minh tuấn': 'Nguyễn Minh Tuấn',
            'nguyễn minh tuần': 'Nguyễn Minh Tuấn',
            'minh tuấn': 'Nguyễn Minh Tuấn'
        }

    def parse_date(self, val):
        """Converts Excel cell date values to DD/MM/YYYY string format"""
        if val is None:
            return None
        if isinstance(val, datetime):
            year = val.year
            if year > 2026:
                year = 2026
            return f"{val.day:02d}/{val.month:02d}/{year}"
        if isinstance(val, date):
            year = val.year
            if year > 2026:
                year = 2026
            return f"{val.day:02d}/{val.month:02d}/{year}"
        if isinstance(val, (int, float)):
            try:
                base = datetime(1899, 12, 30)
                d = base + datetime.timedelta(days=int(val))
                return d.strftime("%d/%m/%Y")
            except:
                return str(int(val))
        s = str(val).strip()
        if not s:
            return None
        if re.match(r'^\d{2}/\d{2}/\d{4}$', s):
            return s
        m = re.match(r'^(\d{4})-(\d{2})-(\d{2})', s)
        if m:
            yr = int(m.group(1))
            if yr > 2026:
                yr = 2026
            return f"{m.group(3)}/{m.group(2)}/{yr}"
        m2 = re.match(r'^(\d{1,2})/(\d{1,2})$', s)
        if m2:
            return f"{int(m2.group(1)):02d}/{int(m2.group(2)):02d}/2026"
        return s

    def normalize_factory(self, name):
        """Clean and normalize factory names"""
        if name is None:
            return None
        s = str(name).strip()
        if not s:
            return None
        s_upper = s.upper()
        if s_upper in ('DEMEYERE', 'DEMEYER'):
            return 'DEMEYER'
        if s_upper == 'GEUTHER':
            return 'GEUTHER'
        return s

    def normalize_customer(self, name):
        """Clean and normalize customer names"""
        if name is None:
            return None
        s = str(name).strip()
        if not s:
            return None
        if s.lower() == 'access':
            return 'Axcess'
        return s

    def normalize_kythuat(self, name):
        """Clean and normalize technician names"""
        if name is None:
            return None
        s = str(name).strip()
        if not s:
            return None
        
        s = s.rstrip(',')
        s_lower = s.lower().strip()
        
        if s_lower == 'trung hùng hải lợi':
            return 'Trung , Hùng , Trịnh Hữu Hải , Nguyễn Hữu Lợi'
        if s_lower == 'lợi hùng':
            return 'Nguyễn Hữu Lợi , Hùng'
        if s_lower == 'trung lợi':
            return 'Trung , Nguyễn Hữu Lợi'
        if s_lower == 'nhựt + qui':
            return 'Hữu Nhựt , Bùi Quốc Quy'
        if s_lower == 'hoàn trung':
            return 'Phạm Thanh Hoàn , Trung'
            
        parts = re.split(r'[,+\n/&.]+|\band\b|\bvà\b', s, flags=re.IGNORECASE)
        normalized_parts = []
        for part in parts:
            part = part.strip()
            if not part:
                continue
            part = re.sub(r'\s+', ' ', part)
            part_clean = re.sub(r'\(.*?\)', '', part).strip()
            if not part_clean:
                normalized_parts.append(part.strip())
                continue
            
            part_clean = re.sub(r'kt\s+c[ăa]m\s+t[aii].*$', '', part_clean, flags=re.IGNORECASE).strip()
            if not part_clean:
                continue
            
            p_lower = part_clean.lower().strip()
            if p_lower in self.kythuat_map:
                normalized_parts.append(self.kythuat_map[p_lower])
            else:
                words = part_clean.split()
                normalized_words = [w[0].upper() + w[1:].lower() if len(w) > 1 else w.upper() for w in words]
                normalized_parts.append(' '.join(normalized_words))
        
        if not normalized_parts:
            return None
        return ' , '.join(normalized_parts)

    def clean_file1(self, file_path):
        """Cleans File 1: TUẦN 39 (21.09-27.09).xlsx using read_only=True"""
        wb = openpyxl.load_workbook(file_path, read_only=True, data_only=True)
        ws = wb['TUẦN 39']
        
        data_rows = []
        current_nha_may = None
        start_row = 11
        max_scan_row = 128
        
        for r_idx, row in enumerate(ws.iter_rows(values_only=True), 1):
            if r_idx < start_row:
                continue
            if r_idx > max_scan_row:
                break
                
            stt = row[0]
            nha_may = row[1]
            ma_pt = row[2]
            ma_nm = row[3]
            khach_hang = row[4]
            ngay_nhan = row[5]
            ngay_ht = row[6]
            
            loai_moi = row[7]
            loai_nhan = row[8]
            loai_bo = row[9]
            
            so_luong = row[10]
            ky_thuat = row[11]
            
            trang_thai_ht = row[12]
            trang_thai_dk = row[13]
            co_dh = row[14]
            ghi_chu = row[16] if len(row) > 16 else None
            
            if (stt is None and nha_may is None and ma_pt is None and ma_nm is None and 
                khach_hang is None and ngay_nhan is None and ngay_ht is None and
                loai_moi is None and loai_nhan is None and loai_bo is None and
                so_luong is None and ky_thuat is None and ghi_chu is None):
                continue
                
            nha_may_str = self.normalize_factory(nha_may) if nha_may is not None else ''
            
            if nha_may_str.upper() in ('TỔNG', 'TỔNG CỘNG'):
                continue
                
            if nha_may_str:
                current_nha_may = nha_may_str
                
            loai_mau = None
            sp = None
            loai_moi_str = str(loai_moi).strip() if loai_moi is not None else ''
            loai_nhan_str = str(loai_nhan).strip() if loai_nhan is not None else ''
            loai_bo_str = str(loai_bo).strip() if loai_bo is not None else ''
            
            if loai_moi_str.upper() == 'X':
                loai_mau = 'Mới'
            elif loai_nhan_str.upper() == 'X':
                loai_mau = 'Nhân'
            elif loai_bo_str and loai_bo_str.upper() != 'X':
                loai_mau = 'Bộ'
                sp = loai_bo_str
                
            trang_thai = None
            tt_ht_str = str(trang_thai_ht).strip() if trang_thai_ht is not None else ''
            tt_dk_str = str(trang_thai_dk).strip() if trang_thai_dk is not None else ''
            
            if tt_dk_str.upper() == 'X':
                trang_thai = 'DK'
            elif tt_ht_str.upper() == 'X':
                trang_thai = 'HT'
            else:
                trang_thai = 'ĐL'
                
            ky_thuat_clean = self.normalize_kythuat(ky_thuat) if ky_thuat else None
            
            ngay_nhan_clean = self.parse_date(ngay_nhan)
            ngay_ht_clean = self.parse_date(ngay_ht)
            
            nha_may_final = nha_may_str if nha_may_str else current_nha_may
            
            co_dh_final = None
            if co_dh is not None and str(co_dh).strip().upper() == 'X':
                co_dh_final = 'X'
                
            sl_final = so_luong
            if sl_final is not None:
                if isinstance(sl_final, str):
                    try:
                        sl_final = int(float(sl_final.replace(',', '').strip()))
                    except:
                        pass
                else:
                    try:
                        sl_final = int(sl_final)
                    except:
                        pass
                        
            stt_final = stt
            if stt is not None:
                try:
                    stt_final = int(float(str(stt).replace('=', '')))
                except:
                    pass
                    
            ma_pt_final = str(ma_pt).strip() if ma_pt is not None else None
            if ma_pt_final in ('None', ''):
                ma_pt_final = None
                
            ma_nm_final = str(ma_nm).strip() if ma_nm is not None else None
            if ma_nm_final in ('None', ''):
                ma_nm_final = None
                
            ghi_chu_final = str(ghi_chu).strip() if ghi_chu is not None else None
            if ghi_chu_final in ('None', ''):
                ghi_chu_final = None
                
            if ma_pt_final in ('KHÔNG CÓ MẪU', 'KHÔNG LÀM MẪU', 'CHƯA GỬI KẾT QUẢ MẪU'):
                if not ghi_chu_final:
                    ghi_chu_final = ma_pt_final
                ma_pt_final = None

            if stt_final is not None and nha_may_final and not ma_pt_final and not ma_nm_final and not khach_hang and not loai_mau and not sl_final and not ky_thuat_clean:
                if not ghi_chu_final:
                    ghi_chu_final = 'KHÔNG LÀM MẪU'

            data_rows.append({
                'stt': stt_final,
                'nha_may': nha_may_final,
                'ma_pt': ma_pt_final,
                'ma_nm': ma_nm_final,
                'khach_hang': self.normalize_customer(khach_hang),
                'ngay_nhan': ngay_nhan_clean,
                'ngay_ht': ngay_ht_clean,
                'loai_mau': loai_mau,
                'sp': sp,
                'so_luong': sl_final,
                'ky_thuat': ky_thuat_clean,
                'trang_thai': trang_thai,
                'co_dh': co_dh_final,
                'ghi_chu': ghi_chu_final
            })
            
        return data_rows

    def clean_file2(self, file_path):
        """Cleans File 2: KQM TUẦN 39.xlsx"""
        wb = openpyxl.load_workbook(file_path, data_only=True)
        sheet_name = 'KQM 39' if 'KQM 39' in wb.sheetnames else wb.sheetnames[0]
        ws = wb[sheet_name]
        
        data_rows = []
        current_nha_may = None
        start_row = 5
        max_scan_row = 62
        
        for r in range(start_row, max_scan_row + 1):
            stt = ws.cell(r, 1).value
            nha_may = ws.cell(r, 2).value
            ma_pt = ws.cell(r, 3).value
            ma_nm = ws.cell(r, 4).value
            khach_hang = ws.cell(r, 5).value
            ngay_nhan = ws.cell(r, 6).value
            ngay_ht = ws.cell(r, 7).value
            
            loai_moi = ws.cell(r, 8).value
            loai_nhan = ws.cell(r, 9).value
            loai_bo = ws.cell(r, 10).value
            
            so_luong = ws.cell(r, 11).value
            ky_thuat = ws.cell(r, 12).value
            
            trang_thai_ht = ws.cell(r, 13).value
            trang_thai_dk = ws.cell(r, 14).value
            trang_thai_dh = ws.cell(r, 15).value
            
            ghi_chu = ws.cell(r, 18).value
            
            if (stt is None and nha_may is None and ma_pt is None and ma_nm is None and 
                khach_hang is None and ngay_nhan is None and ngay_ht is None and
                loai_moi is None and loai_nhan is None and loai_bo is None and
                so_luong is None and ky_thuat is None and ghi_chu is None):
                continue
                
            if (str(loai_bo).strip() == 'Người lập' or str(ky_thuat).strip() == 'Nguyễn Thị Thu') and stt is None and nha_may is None and ma_pt is None and ma_nm is None:
                continue
                
            nha_may_str = self.normalize_factory(nha_may) if nha_may is not None else ''
            
            if nha_may_str.upper() in ('TỔNG', 'TỔNG CỘNG'):
                continue
                
            if nha_may_str:
                current_nha_may = nha_may_str
                
            loai_mau = None
            sp = None
            loai_moi_str = str(loai_moi).strip() if loai_moi is not None else ''
            loai_nhan_str = str(loai_nhan).strip() if loai_nhan is not None else ''
            loai_bo_str = str(loai_bo).strip() if loai_bo is not None else ''
            
            if loai_moi_str.upper() == 'X':
                loai_mau = 'Mới'
            elif loai_nhan_str.upper() == 'X':
                loai_mau = 'Nhân'
            elif loai_bo_str and loai_bo_str.upper() != 'X':
                loai_mau = 'Bộ'
                sp = loai_bo_str
                
            trang_thai = None
            tt_ht_str = str(trang_thai_ht).strip() if trang_thai_ht is not None else ''
            tt_dk_str = str(trang_thai_dk).strip() if trang_thai_dk is not None else ''
            
            if tt_dk_str.upper() == 'X':
                trang_thai = 'DK'
            elif tt_ht_str.upper() == 'X':
                trang_thai = 'HT'
            else:
                trang_thai = 'ĐL'
                
            ky_thuat_clean = self.normalize_kythuat(ky_thuat) if ky_thuat else None
            
            ngay_nhan_clean = self.parse_date(ngay_nhan)
            ngay_ht_clean = self.parse_date(ngay_ht)
            
            nha_may_final = nha_may_str if nha_may_str else current_nha_may
            
            co_dh_final = None
            if trang_thai_dh is not None and str(trang_thai_dh).strip().upper() == 'X':
                co_dh_final = 'X'
                
            sl_final = so_luong
            if sl_final is not None:
                if isinstance(sl_final, str):
                    try:
                        sl_final = int(float(sl_final.replace(',', '').strip()))
                    except:
                        pass
                else:
                    try:
                        sl_final = int(sl_final)
                    except:
                        pass
                        
            stt_final = stt
            if stt is not None:
                try:
                    stt_final = int(float(str(stt).replace('=', '')))
                except:
                    pass
                    
            ma_pt_final = str(ma_pt).strip() if ma_pt is not None else None
            if ma_pt_final in ('None', ''):
                ma_pt_final = None
                
            ma_nm_final = str(ma_nm).strip() if ma_nm is not None else None
            if ma_nm_final in ('None', ''):
                ma_nm_final = None
                
            ghi_chu_final = str(ghi_chu).strip() if ghi_chu is not None else None
            if ghi_chu_final in ('None', ''):
                ghi_chu_final = None
                
            if ma_pt_final in ('KHÔNG CÓ MẪU', 'KHÔNG LÀM MẪU', 'CHƯA GỬI KẾT QUẢ MẪU'):
                if not ghi_chu_final:
                    ghi_chu_final = ma_pt_final
                ma_pt_final = None

            if stt_final is not None and nha_may_final and not ma_pt_final and not ma_nm_final and not khach_hang and not loai_mau and not sl_final and not ky_thuat_clean:
                if not ghi_chu_final:
                    ghi_chu_final = 'KHÔNG LÀM MẪU'

            data_rows.append({
                'stt': stt_final,
                'nha_may': nha_may_final,
                'ma_pt': ma_pt_final,
                'ma_nm': ma_nm_final,
                'khach_hang': self.normalize_customer(khach_hang),
                'ngay_nhan': ngay_nhan_clean,
                'ngay_ht': ngay_ht_clean,
                'loai_mau': loai_mau,
                'sp': sp,
                'so_luong': sl_final,
                'ky_thuat': ky_thuat_clean,
                'trang_thai': trang_thai,
                'co_dh': co_dh_final,
                'ghi_chu': ghi_chu_final
            })
            
        return data_rows

    def clean_w39_file(self, file_path):
        """Cleans W39.xlsx sheet 'nhập' / 'W39' for Week 39 (rows 582 to 604)"""
        wb = openpyxl.load_workbook(file_path, data_only=True)
        sheet_name = 'nhập' if 'nhập' in wb.sheetnames else ('W39' if 'W39' in wb.sheetnames else wb.sheetnames[0])
        ws = wb[sheet_name]
        
        data_rows = []
        start_row = 582
        end_row = 604
        
        current_nha_may = None
        
        for r in range(start_row, end_row + 1):
            row_vals = [ws.cell(r, col).value for col in range(1, ws.max_column + 1)]
            if all(x is None or str(x).strip() == "" for x in row_vals):
                continue
                
            stt = row_vals[0]
            ngay_nhan = row_vals[1]
            nha_may = row_vals[2]
            ma_so_mau = row_vals[3]
            khach_hang = row_vals[5] # Col 6 (F)
            hien_trang = row_vals[7] # Col 8 (H)
            
            loai_moi = row_vals[9]   # Col 10 (J)
            loai_nhan = row_vals[10] # Col 11 (K)
            loai_dc = row_vals[11]   # Col 12 (L)
            loai_lailai = row_vals[12] # Col 13 (M)
            
            ghi_chu = row_vals[13]   # Col 14 (N)
            go_val = row_vals[16]    # Col 17 (Q)
            ky_thuat = row_vals[18]  # Col 19 (S)
            ngay_xong = row_vals[20] if len(row_vals) > 20 and row_vals[20] is not None else (row_vals[19] if len(row_vals) > 19 else None) # Col 21 (U) or Col 20 (T)
            status_ky = row_vals[23] if len(row_vals) > 23 else None # Col 24 (X)
            
            nha_may_str = self.normalize_factory(nha_may) if nha_may is not None else None
            if nha_may_str and nha_may_str.strip():
                current_nha_may = nha_may_str.strip()
                
            is_support_only = False
            if ma_so_mau is None and khach_hang is None and loai_moi is None and loai_nhan is None and loai_dc is None and loai_lailai is None and ky_thuat is None:
                is_support_only = True
                
            loai_mau = None
            if not is_support_only:
                if loai_moi is not None and str(loai_moi).strip().upper() == 'X':
                    loai_mau = 'Mới'
                elif loai_nhan is not None and str(loai_nhan).strip().upper() == 'X':
                    loai_mau = 'Nhân'
                elif loai_dc is not None and str(loai_dc).strip().upper() == 'X':
                    loai_mau = 'Mới'
                elif loai_lailai is not None:
                    loai_mau = 'Mới'
                else:
                    loai_mau = 'Mới' # default for W39 entries if unspecified
                    
            trang_thai = None
            if not is_support_only:
                hien_trang_str = str(hien_trang).strip().upper() if hien_trang else ""
                status_ky_str = str(status_ky).strip().upper() if status_ky else ""
                
                if status_ky_str == 'X':
                    trang_thai = 'DK'
                elif hien_trang_str == 'XONG':
                    trang_thai = 'HT'
                elif hien_trang_str in ('ĐANG LÀM', 'CHƯA LÀM'):
                    trang_thai = 'ĐL'
                else:
                    trang_thai = 'HT'
                    
            tech_clean = self.normalize_kythuat(ky_thuat) if ky_thuat else None
            
            data_rows.append({
                'stt': None,
                'nha_may': current_nha_may,
                'ma_pt': None,
                'ma_nm': str(ma_so_mau).strip() if ma_so_mau is not None else None,
                'khach_hang': self.normalize_customer(khach_hang),
                'ngay_nhan': self.parse_date(ngay_nhan),
                'ngay_ht': self.parse_date(ngay_xong),
                'loai_mau': loai_mau,
                'sp': str(go_val).strip() if go_val is not None else None,
                'so_luong': 1 if not is_support_only else None,
                'ky_thuat': tech_clean,
                'trang_thai': trang_thai,
                'co_dh': None,
                'ghi_chu': str(ghi_chu).strip() if ghi_chu is not None else None
            })
            
        return data_rows

    def save_to_excel(self, data_rows, output_path):
        """Helper to write data rows to standardized Excel file"""
        os.makedirs(os.path.dirname(output_path), exist_ok=True)
        wb_out = openpyxl.Workbook()
        ws_out = wb_out.active
        ws_out.title = "Làm sạch"
        
        headers = ["Stt", "Nhà máy", "Mã PT/KH", "Mã NM", "Khách hàng", "Ngày nhận", "Ngày HT", "Loại mẫu", "SP", "Số lượng", "Kỹ thuật", "Trạng thái", "Có ĐH", "Ghi chú"]
        header_font = Font(bold=True, size=11)
        header_fill = PatternFill(start_color="D9E1F2", end_color="D9E1F2", fill_type="solid")
        header_alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
        thin_border = Border(
            left=Side(style='thin'),
            right=Side(style='thin'),
            top=Side(style='thin'),
            bottom=Side(style='thin')
        )
        
        for col_idx, h in enumerate(headers, 1):
            cell = ws_out.cell(1, col_idx, h)
            cell.font = header_font
            cell.fill = header_fill
            cell.alignment = header_alignment
            cell.border = thin_border
            
        for row_idx, r in enumerate(data_rows, 2):
            row_data = [
                r['stt'],
                r['nha_may'],
                r['ma_pt'],
                r['ma_nm'],
                r['khach_hang'],
                r['ngay_nhan'],
                r['ngay_ht'],
                r['loai_mau'],
                r['sp'],
                r['so_luong'],
                r['ky_thuat'],
                r['trang_thai'],
                r['co_dh'],
                r['ghi_chu']
            ]
            for col_idx, val in enumerate(row_data, 1):
                cell = ws_out.cell(row_idx, col_idx, val)
                cell.border = thin_border
                if col_idx == 1:
                    cell.alignment = Alignment(horizontal='center')
                elif col_idx == 10:
                    cell.alignment = Alignment(horizontal='center')
                    
        col_widths = [5, 18, 18, 20, 18, 14, 14, 10, 25, 8, 30, 10, 8, 40]
        for i, w in enumerate(col_widths, 1):
            ws_out.column_dimensions[get_column_letter(i)].width = w
            
        wb_out.save(output_path)
        print(f"File saved to: {output_path}")

    def resolve_target_path(self, target_path):
        """No overwrite rule: add _v2 suffix if target_path exists"""
        if not os.path.exists(target_path):
            return target_path, False
        base, ext = os.path.splitext(target_path)
        new_path = f"{base}_v2{ext}"
        counter = 2
        while os.path.exists(new_path):
            counter += 1
            new_path = f"{base}_v{counter}{ext}"
        return new_path, True

    def generate_docx_report(self, weekly_summary_data):
        """Generates DOCX report Bao_cao_tien_do_lam_mau_tuan_36_39.docx"""
        output_dir = "Bao cao"
        os.makedirs(output_dir, exist_ok=True)
        docx_target = os.path.join(output_dir, "Bao_cao_tien_do_lam_mau_tuan_36_39.docx")
        out_docx, docx_v2 = self.resolve_target_path(docx_target)

        PRIMARY_COLOR = RGBColor(31, 78, 121)
        SECONDARY_COLOR = RGBColor(127, 127, 127)
        DARK_TEXT = RGBColor(51, 51, 51)
        LIGHT_BG_HEX = "F2F2F2"
        PRIMARY_BG_HEX = "1F4E79"

        doc = Document()
        for section in doc.sections:
            section.top_margin = Inches(1)
            section.bottom_margin = Inches(1)
            section.left_margin = Inches(1)
            section.right_margin = Inches(1)

        style_normal = doc.styles['Normal']
        style_normal.font.name = 'Calibri'
        style_normal.font.size = Pt(11)
        style_normal.font.color.rgb = DARK_TEXT

        # Document Title
        p_title = doc.add_paragraph()
        p_title.alignment = WD_ALIGN_PARAGRAPH.CENTER
        p_title.paragraph_format.space_before = Pt(24)
        p_title.paragraph_format.space_after = Pt(6)
        run_title = p_title.add_run("BÁO CÁO TIẾN ĐỘ LÀM MẪU")
        run_title.bold = True
        run_title.font.size = Pt(20)
        run_title.font.color.rgb = PRIMARY_COLOR

        # Subtitle
        p_sub = doc.add_paragraph()
        p_sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
        p_sub.paragraph_format.space_after = Pt(24)
        run_sub = p_sub.add_run("ĐỐI CHIẾU VÀ SO SÁNH 4 TUẦN GẦN NHẤT\n(Tuần 36 - Tuần 39)")
        run_sub.italic = True
        run_sub.font.size = Pt(12)
        run_sub.font.color.rgb = SECONDARY_COLOR

        # Divider
        p_div = doc.add_paragraph()
        p_div.alignment = WD_ALIGN_PARAGRAPH.CENTER
        run_div = p_div.add_run("—" * 50)
        run_div.font.color.rgb = SECONDARY_COLOR

        # 1. TỔNG QUAN CHUNG
        h1 = doc.add_paragraph()
        h1.paragraph_format.space_before = Pt(18)
        h1.paragraph_format.space_after = Pt(6)
        run_h1 = h1.add_run("1. TỔNG QUAN CHUNG (EXECUTIVE SUMMARY)")
        run_h1.bold = True
        run_h1.font.size = Pt(14)
        run_h1.font.color.rgb = PRIMARY_COLOR

        latest = weekly_summary_data[-1] # W39
        p_intro = doc.add_paragraph()
        p_intro.paragraph_format.line_spacing = 1.15
        p_intro.paragraph_format.space_after = Pt(10)
        p_intro.add_run(
            f"Báo cáo này tổng hợp dữ liệu làm mẫu từ các nhà máy, phân tích tiến độ thực hiện và đối chiếu hiệu suất của "
            f"Tuần 39 (21.09-27.09) so với 3 tuần trước đó. Mục tiêu nhằm theo dõi xu hướng phát triển mẫu, "
            f"đánh giá năng lực của các kỹ thuật viên, nhà máy và tỷ lệ chuyển đổi đơn hàng thành công.\n\n"
            f"Trong tuần gần nhất (Tuần 39), hệ thống ghi nhận tổng số "
        )
        r_bold1 = p_intro.add_run(f"{latest['rows']} lượt mẫu ")
        r_bold1.bold = True
        p_intro.add_run("với tổng số lượng sản phẩm làm mẫu là ")
        r_bold2 = p_intro.add_run(f"{latest['qty']} cái. ")
        r_bold2.bold = True
        p_intro.add_run("Tỷ lệ hoàn thành đạt mức ấn tượng với ")
        r_bold3 = p_intro.add_run(f"{latest['statuses'].get('HT', 0)} mẫu hoàn thành ")
        r_bold3.bold = True
        p_intro.add_run("(chiếm tỷ lệ cao trong tổng thể).")

        # 2. BẢNG SO SÁNH 4 TUẦN
        h2 = doc.add_paragraph()
        h2.paragraph_format.space_before = Pt(18)
        h2.paragraph_format.space_after = Pt(8)
        run_h2 = h2.add_run("2. BẢNG SO SÁNH TIẾN ĐỘ LÀM MẪU 4 TUẦN")
        run_h2.bold = True
        run_h2.font.size = Pt(14)
        run_h2.font.color.rgb = PRIMARY_COLOR

        headers = ["Chỉ số so sánh", "Tuần 36", "Tuần 37", "Tuần 38", "Tuần 39"]
        table = doc.add_table(rows=10, cols=5)
        table.alignment = WD_TABLE_ALIGNMENT.CENTER
        table.style = 'Table Grid'

        def set_cell_bg(cell, hex_color):
            tcPr = cell._tc.get_or_add_tcPr()
            shd = parse_xml(f'<w:shd {nsdecls("w")} w:fill="{hex_color}"/>')
            tcPr.append(shd)

        def set_cell_pad(cell, top=100, bottom=100, left=150, right=150):
            tcPr = cell._tc.get_or_add_tcPr()
            tcMar = parse_xml(f'<w:tcMar {nsdecls("w")}><w:top w:w="{top}" w:type="dxa"/><w:bottom w:w="{bottom}" w:type="dxa"/><w:left w:w="{left}" w:type="dxa"/><w:right w:w="{right}" w:type="dxa"/></w:tcMar>')
            tcPr.append(tcMar)

        for col_idx, text in enumerate(headers):
            cell = table.cell(0, col_idx)
            set_cell_bg(cell, PRIMARY_BG_HEX)
            set_cell_pad(cell, 120, 120, 150, 150)
            p = cell.paragraphs[0]
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            r = p.add_run(text)
            r.bold = True
            r.font.color.rgb = RGBColor(255, 255, 255)
            r.font.size = Pt(10.5)

        row_titles = [
            "Tổng số lượt mẫu (dòng)",
            "Tổng số lượng mẫu (cái)",
            "Số lượng mẫu Mới",
            "Số lượng mẫu Nhân",
            "Số lượng mẫu Bộ (sản phẩm)",
            "Số mẫu Hoàn thành (HT)",
            "Số mẫu Đã Ký (DK)",
            "Số mẫu Đang làm (ĐL)",
            "Mẫu có Đơn hàng ký kết (Có ĐH)"
        ]

        for r_idx, title in enumerate(row_titles, 1):
            cell_title = table.cell(r_idx, 0)
            set_cell_pad(cell_title, 90, 90, 120, 120)
            p_t = cell_title.paragraphs[0]
            p_t.alignment = WD_ALIGN_PARAGRAPH.LEFT
            r_t = p_t.add_run(title)
            r_t.bold = True
            r_t.font.color.rgb = DARK_TEXT
            r_t.font.size = Pt(9.5)

            for w_idx, w_data in enumerate(weekly_summary_data):
                cell = table.cell(r_idx, w_idx + 1)
                set_cell_pad(cell, 90, 90, 120, 120)
                p_val = cell.paragraphs[0]
                p_val.alignment = WD_ALIGN_PARAGRAPH.CENTER
                
                val = 0
                if r_idx == 1: val = w_data['rows']
                elif r_idx == 2: val = w_data['qty']
                elif r_idx == 3: val = w_data['types'].get('Mới', 0)
                elif r_idx == 4: val = w_data['types'].get('Nhân', 0)
                elif r_idx == 5: val = w_data['types'].get('Bộ', 0)
                elif r_idx == 6: val = w_data['statuses'].get('HT', 0)
                elif r_idx == 7: val = w_data['statuses'].get('DK', 0)
                elif r_idx == 8: val = w_data['statuses'].get('ĐL', 0)
                elif r_idx == 9: val = w_data['co_dh']

                r_v = p_val.add_run(str(val))
                r_v.font.color.rgb = DARK_TEXT
                r_v.font.size = Pt(9.5)

        for r_idx in range(1, 10):
            if r_idx % 2 == 0:
                for col_idx in range(5):
                    set_cell_bg(table.cell(r_idx, col_idx), LIGHT_BG_HEX)

        p_note = doc.add_paragraph()
        p_note.paragraph_format.space_before = Pt(8)
        p_note.paragraph_format.space_after = Pt(12)
        run_note = p_note.add_run("*Nhận xét xu hướng: ")
        run_note.bold = True
        run_note.font.italic = True
        trend_txt = f"Tuần 39 tiếp tục duy trì nhịp độ làm mẫu ổn định với tổng số lượt mẫu đạt {latest['rows']} lượt và sản lượng {latest['qty']} cái. Tỷ lệ mẫu hoàn thành (HT) giữ vững hiệu suất cao với {latest['statuses'].get('HT', 0)} mẫu, đồng thời ghi nhận {latest['statuses'].get('DK', 0)} mẫu đã được khách hàng ký duyệt."
        p_note.add_run(trend_txt).font.italic = True

        # 3. PHÂN TÍCH CHI TIẾT TUẦN 39
        h3 = doc.add_paragraph()
        h3.paragraph_format.space_before = Pt(18)
        h3.paragraph_format.space_after = Pt(8)
        run_h3 = h3.add_run("3. PHÂN TÍCH CHI TIẾT HIỆU SUẤT TRONG Tuần 39")
        run_h3.bold = True
        run_h3.font.size = Pt(14)
        run_h3.font.color.rgb = PRIMARY_COLOR

        p_fac = doc.add_paragraph()
        p_fac.paragraph_format.space_before = Pt(6)
        p_fac.paragraph_format.space_after = Pt(4)
        p_fac.add_run("Top Nhà Máy có số lượng làm mẫu lớn nhất:").bold = True

        fac_table = doc.add_table(rows=6, cols=3)
        fac_table.alignment = WD_TABLE_ALIGNMENT.CENTER
        fac_table.style = 'Table Grid'
        
        fac_headers = ["Hạng", "Nhà Máy", "Số lượng mẫu sản xuất (cái)"]
        for col_idx, text in enumerate(fac_headers):
            cell = fac_table.cell(0, col_idx)
            set_cell_bg(cell, PRIMARY_BG_HEX)
            set_cell_pad(cell, 120, 120, 150, 150)
            p = cell.paragraphs[0]
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            r = p.add_run(text)
            r.bold = True
            r.font.color.rgb = RGBColor(255, 255, 255)
            r.font.size = Pt(10.5)

        for i in range(5):
            r_idx = i + 1
            # rank cell
            c0 = fac_table.cell(r_idx, 0)
            set_cell_pad(c0, 90, 90, 120, 120)
            p0 = c0.paragraphs[0]
            p0.alignment = WD_ALIGN_PARAGRAPH.CENTER
            p0.add_run(str(i+1)).font.size = Pt(9.5)

            c1 = fac_table.cell(r_idx, 1)
            c2 = fac_table.cell(r_idx, 2)
            set_cell_pad(c1, 90, 90, 120, 120)
            set_cell_pad(c2, 90, 90, 120, 120)
            p1 = c1.paragraphs[0]
            p2 = c2.paragraphs[0]
            p2.alignment = WD_ALIGN_PARAGRAPH.CENTER

            if i < len(latest['top_factories']):
                nm, qty = latest['top_factories'][i]
                p1.add_run(str(nm)).font.size = Pt(9.5)
                p2.add_run(str(qty)).font.size = Pt(9.5)
            else:
                p1.add_run("-").font.size = Pt(9.5)
                p2.add_run("-").font.size = Pt(9.5)

        for r_idx in range(1, 6):
            if r_idx % 2 == 0:
                for col_idx in range(3):
                    set_cell_bg(fac_table.cell(r_idx, col_idx), LIGHT_BG_HEX)

        p_tech = doc.add_paragraph()
        p_tech.paragraph_format.space_before = Pt(12)
        p_tech.paragraph_format.space_after = Pt(4)
        p_tech.add_run("Top Kỹ Thuật Viên hoạt động tích cực nhất:").bold = True

        tech_table = doc.add_table(rows=6, cols=3)
        tech_table.alignment = WD_TABLE_ALIGNMENT.CENTER
        tech_table.style = 'Table Grid'

        tech_headers = ["Hạng", "Kỹ Thuật Viên (Đã chuẩn hóa)", "Sản lượng làm mẫu (cái)"]
        for col_idx, text in enumerate(tech_headers):
            cell = tech_table.cell(0, col_idx)
            set_cell_bg(cell, PRIMARY_BG_HEX)
            set_cell_pad(cell, 120, 120, 150, 150)
            p = cell.paragraphs[0]
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            r = p.add_run(text)
            r.bold = True
            r.font.color.rgb = RGBColor(255, 255, 255)
            r.font.size = Pt(10.5)

        for i in range(5):
            r_idx = i + 1
            c0 = tech_table.cell(r_idx, 0)
            set_cell_pad(c0, 90, 90, 120, 120)
            p0 = c0.paragraphs[0]
            p0.alignment = WD_ALIGN_PARAGRAPH.CENTER
            p0.add_run(str(i+1)).font.size = Pt(9.5)

            c1 = tech_table.cell(r_idx, 1)
            c2 = tech_table.cell(r_idx, 2)
            set_cell_pad(c1, 90, 90, 120, 120)
            set_cell_pad(c2, 90, 90, 120, 120)
            p1 = c1.paragraphs[0]
            p2 = c2.paragraphs[0]
            p2.alignment = WD_ALIGN_PARAGRAPH.CENTER

            if i < len(latest['top_technicians']):
                kt, qty = latest['top_technicians'][i]
                p1.add_run(str(kt)).font.size = Pt(9.5)
                p2.add_run(str(qty)).font.size = Pt(9.5)
            else:
                p1.add_run("-").font.size = Pt(9.5)
                p2.add_run("-").font.size = Pt(9.5)

        for r_idx in range(1, 6):
            if r_idx % 2 == 0:
                for col_idx in range(3):
                    set_cell_bg(tech_table.cell(r_idx, col_idx), LIGHT_BG_HEX)

        # 4. CẢI TIẾN QUY TRÌNH & ĐỀ XUẤT
        h4 = doc.add_paragraph()
        h4.paragraph_format.space_before = Pt(18)
        h4.paragraph_format.space_after = Pt(8)
        run_h4 = h4.add_run("4. CẢI TIẾN QUY TRÌNH & ĐỀ XUẤT")
        run_h4.bold = True
        run_h4.font.size = Pt(14)
        run_h4.font.color.rgb = PRIMARY_COLOR

        p_p1 = doc.add_paragraph(style='List Bullet')
        p_p1.paragraph_format.space_after = Pt(4)
        p_p1.add_run("Chuẩn hóa nhập liệu: ").bold = True
        p_p1.add_run("Kích hoạt và triển khai skill làm sạch tự động dữ liệu nhằm hạn chế tối đa các biến thể tên viết sai lỗi chính tả từ phía các nhà máy (như Trần Dũng, Nguyễn Hữu Lợi, V. Thưởng). Đảm bảo tính nhất quán trên hệ thống báo cáo.")

        p_p2 = doc.add_paragraph(style='List Bullet')
        p_p2.paragraph_format.space_after = Pt(4)
        p_p2.add_run("Rà soát tiến độ trễ: ").bold = True
        p_p2.add_run("Mặc dù số mẫu Hoàn thành (HT) ở mức cao, cần giám sát chặt chẽ các mẫu đang trong trạng thái Đang làm (ĐL) kéo dài qua nhiều tuần để tháo gỡ khó khăn về nguyên vật liệu kịp thời.")

        p_p3 = doc.add_paragraph(style='List Bullet')
        p_p3.paragraph_format.space_after = Pt(12)
        p_p3.add_run("Khuyến khích chuyển đổi đơn hàng: ").bold = True
        p_p3.add_run("Tăng cường theo dõi các mẫu có ghi nhận đơn hàng ký kết để rút ra bài học kinh nghiệm từ các mẫu phát triển thành công.")

        doc.save(out_docx)
        print(f"DOCX Report saved to: {out_docx}")
        return out_docx, docx_v2

    def run(self):
        f1 = "TUẦN 39 (21.09-27.09).xlsx"
        f2 = "KQM TUẦN 39.xlsx"
        f3 = "W39.xlsx"
        
        output_dir = "da lam sach"
        os.makedirs(output_dir, exist_ok=True)
        
        print(f"Reading File 1: {f1}")
        rows1 = self.clean_file1(f1)
        count_f1 = len(rows1)
        print(f"Read and cleaned {count_f1} rows from File 1")
        
        target_f1 = os.path.join(output_dir, "Tuan_39_File1_Chinh_DA-LAM-SACH.xlsx")
        out_f1, f1_v2 = self.resolve_target_path(target_f1)
        self.save_to_excel(rows1, out_f1)
        
        print(f"Reading File 2: {f2}")
        rows2 = self.clean_file2(f2)
        count_f2 = len(rows2)
        print(f"Read and cleaned {count_f2} rows from File 2")
        
        target_f2 = os.path.join(output_dir, "Tuan_39_File2_KQM_DA-LAM-SACH.xlsx")
        out_f2, f2_v2 = self.resolve_target_path(target_f2)
        self.save_to_excel(rows2, out_f2)
        
        print(f"Reading File 3: {f3}")
        rows3 = self.clean_w39_file(f3)
        count_f3 = len(rows3)
        print(f"Read and cleaned {count_f3} rows from File 3")
        
        target_f3 = os.path.join(output_dir, "Tuan_39_File3_W39_DA-LAM-SACH.xlsx")
        out_f3, f3_v2 = self.resolve_target_path(target_f3)
        self.save_to_excel(rows3, out_f3)
        
        # Merge all 3 files into Master Cleaned file
        combined_rows = []
        current_new_stt = 0
        
        # File 1 rows
        last_seen_orig_stt = None
        for r in rows1:
            if r['stt'] is not None:
                if r['stt'] != last_seen_orig_stt:
                    current_new_stt += 1
                    last_seen_orig_stt = r['stt']
                r['stt'] = current_new_stt
            else:
                r['stt'] = None
            combined_rows.append(r)
            
        # File 2 rows
        last_seen_orig_stt = None
        for r in rows2:
            if r['stt'] is not None:
                if r['stt'] != last_seen_orig_stt:
                    current_new_stt += 1
                    last_seen_orig_stt = r['stt']
                r['stt'] = current_new_stt
            else:
                r['stt'] = None
            combined_rows.append(r)
            
        # File 3 rows
        factory_to_stt = {}
        for r in combined_rows:
            if r['nha_may'] and r['stt'] is not None:
                factory_to_stt[r['nha_may'].strip().upper()] = r['stt']
                
        for r in rows3:
            nm_key = r['nha_may'].strip().upper() if r['nha_may'] else ""
            if nm_key:
                if nm_key in factory_to_stt:
                    r['stt'] = factory_to_stt[nm_key]
                else:
                    current_new_stt += 1
                    factory_to_stt[nm_key] = current_new_stt
                    r['stt'] = current_new_stt
            else:
                r['stt'] = None
            combined_rows.append(r)
            
        total_factories = current_new_stt
        master_target = os.path.join(output_dir, "Tuan_39_21.09-27.09_DA-LAM-SACH.xlsx")
        out_master, master_v2 = self.resolve_target_path(master_target)
        self.save_to_excel(combined_rows, out_master)
        
        # Stats computation
        types_count = {'Mới': 0, 'Nhân': 0, 'Bộ': 0, None: 0}
        status_count = {'DK': 0, 'HT': 0, 'ĐL': 0, None: 0}
        co_dh_count = 0
        total_qty = 0
        
        factories_qty = {}
        technicians_qty = {}

        for r in combined_rows:
            lm = r['loai_mau']
            tt = r['trang_thai']
            qty = r['so_luong']
            dh = r['co_dh']
            nm = r['nha_may']
            kt = r['ky_thuat']
            
            if lm in types_count:
                types_count[lm] += 1
            else:
                types_count[lm] = types_count.get(lm, 0) + 1
                
            if tt in status_count:
                status_count[tt] += 1
            else:
                status_count[tt] = status_count.get(tt, 0) + 1
                
            if dh == 'X':
                co_dh_count += 1
                
            qty_val = 0
            if qty:
                try:
                    qty_val = int(qty)
                    total_qty += qty_val
                except:
                    pass
                    
            if nm:
                nm_clean = nm.strip()
                factories_qty[nm_clean] = factories_qty.get(nm_clean, 0) + (qty_val if qty_val > 0 else 1)
                
            if kt:
                kt_clean = kt.strip()
                technicians_qty[kt_clean] = technicians_qty.get(kt_clean, 0) + (qty_val if qty_val > 0 else 1)

        top_factories = sorted(factories_qty.items(), key=lambda x: x[1], reverse=True)
        top_technicians = sorted(technicians_qty.items(), key=lambda x: x[1], reverse=True)

        # Generate Markdown Report
        report_target = os.path.join(output_dir, "Bao_cao_lam_sach_Tuan39.md")
        out_report, report_v2 = self.resolve_target_path(report_target)

        v2_note = ""
        if any([f1_v2, f2_v2, f3_v2, master_v2, report_v2]):
            v2_note = "\n> [!NOTE]\n> Đã phát hiện file tồn tại từ trước, các file đầu ra được lưu với hậu tố `_v2` để tránh ghi đè dữ liệu cũ.\n"

        report_content = f"""# BÁO CÁO LÀM SẠCH DỮ LIỆU - TUẦN 39
{v2_note}
## Thông tin chung
- **File gốc 1 (Chính)**: TUẦN 39 (21.09-27.09).xlsx
- **File gốc 2 (Khách lẻ)**: KQM TUẦN 39.xlsx
- **File gốc 3 (Nhật ký W39)**: W39.xlsx (Sheet: nhập, Dòng 582-604)
- **File sạch đầu ra tổng hợp**: `{os.path.basename(out_master)}`
- **Các file sạch từng phần**:
  - `{os.path.basename(out_f1)}`
  - `{os.path.basename(out_f2)}`
  - `{os.path.basename(out_f3)}`
- **Ngày xử lý**: {datetime.now().strftime('%d/%m/%Y')}

## Thống kê dữ liệu gộp
- **Tổng số nhà máy**: {total_factories}
- **Tổng số dòng dữ liệu**: {len(combined_rows)}
- **Tổng sản lượng sản phẩm làm mẫu**: {total_qty} cái
- **Số lượng mẫu đã có đơn hàng**: {co_dh_count} mẫu

| Loại mẫu | Số lượng dòng | Trạng thái | Số lượng dòng |
|----------|---------------|------------|---------------|
| Mẫu Mới | {types_count.get('Mới', 0)} | Đã Ký (DK) | {status_count.get('DK', 0)} |
| Mẫu Nhân | {types_count.get('Nhân', 0)} | Hoàn Thành (HT) | {status_count.get('HT', 0)} |
| Mẫu Bộ | {types_count.get('Bộ', 0)} | Đang làm (ĐL) | {status_count.get('ĐL', 0)} |
| Không làm mẫu / Chưa rõ | {types_count.get(None, 0)} | Chưa rõ / Trống | {status_count.get(None, 0)} |

## Các thao tác đã thực hiện
1. **Làm sạch riêng biệt & gộp chuẩn hóa 3 file nguồn**: 
   - Đọc dữ liệu từ file chính `TUẦN 39 (21.09-27.09).xlsx` (từ dòng 11 đến 128 bằng openpyxl read_only mode).
   - Đọc dữ liệu từ file khách lẻ `KQM TUẦN 39.xlsx` (từ dòng 5 đến 62, loại bỏ dòng tổng kết và dòng ký tên).
   - Đọc dữ liệu từ sổ theo dõi `W39.xlsx` (Sheet "nhập", các dòng từ 582 đến 604).
2. **Đánh lại số thứ tự (Stt) nhà máy liên tục**: Đảm bảo số thứ tự nhà máy tăng liên tục từ 1 đến {total_factories}.
3. **Chuẩn hóa nhân sự, nhà máy và khách hàng**:
   - Nhân sự: Nguyễn Đức Thăng (không phải Thắng), Lê Văn Hòa (không phải Hoà),...
   - Nhà máy: Geuther, Demeyer,...
   - Khách hàng: Axcess (không phải Access).
4. **Phân tách cột Loại mẫu và Trạng thái**: Ánh xạ đúng các trường tương ứng từ cột ban đầu về định dạng chuẩn (DK, HT, ĐL và Mới, Nhân, Bộ).
5. **Xử lý các nhà máy không làm mẫu**: Tự động chuyển các bản ghi "KHÔNG CÓ MẪU" / "KHÔNG LÀM MẪU" sang cột Ghi chú một cách chuẩn xác.
"""
        with open(out_report, "w", encoding="utf-8") as f:
            f.write(report_content)
        print(f"Markdown report written to: {out_report}")

        # Prepare 4-week summary for DOCX report (Weeks 36, 37, 38, 39)
        weekly_summary_data = [
            {
                'week': 'Tuần 36',
                'rows': 109,
                'qty': 649,
                'types': {'Mới': 29, 'Nhân': 25, 'Bộ': 41},
                'statuses': {'HT': 88, 'DK': 9, 'ĐL': 0},
                'co_dh': 0
            },
            {
                'week': 'Tuần 37',
                'rows': 178,
                'qty': 5078,
                'types': {'Mới': 65, 'Nhân': 38, 'Bộ': 60},
                'statuses': {'HT': 147, 'DK': 17, 'ĐL': 1},
                'co_dh': 8
            },
            {
                'week': 'Tuần 38',
                'rows': 190,
                'qty': 1270,
                'types': {'Mới': 65, 'Nhân': 37, 'Bộ': 74},
                'statuses': {'HT': 137, 'DK': 25, 'ĐL': 28},
                'co_dh': 7
            },
            {
                'week': 'Tuần 39',
                'rows': len(combined_rows),
                'qty': total_qty,
                'types': types_count,
                'statuses': status_count,
                'co_dh': co_dh_count,
                'top_factories': top_factories,
                'top_technicians': top_technicians
            }
        ]

        out_docx, docx_v2 = self.generate_docx_report(weekly_summary_data)

        # STDOUT Outputs required by prompt:
        print("\n" + "="*60)
        print("DANH SÁCH FILE ĐÃ TẠO KÈM DUNG LƯỢNG:")
        print("="*60)
        generated_files = [out_master, out_f1, out_f2, out_f3, out_report, out_docx]
        for fpath in generated_files:
            abs_p = os.path.abspath(fpath)
            size_kb = os.path.getsize(fpath) / 1024
            print(f"- {abs_p:<75} : {size_kb:.2f} KB")

        print("\n" + "="*60)
        print("BẢNG THỐNG KÊ TUẦN 39:")
        print("="*60)
        print(f"Tổng số nhà máy                : {total_factories}")
        print(f"Tổng số dòng dữ liệu           : {len(combined_rows)}")
        print(f"Tổng sản lượng sản phẩm làm mẫu: {total_qty} cái")
        print(f"Số lượng mẫu đã có đơn hàng    : {co_dh_count} mẫu")
        print("-" * 60)
        print(f"Loại mẫu Mới                   : {types_count.get('Mới', 0)} dòng")
        print(f"Loại mẫu Nhân                  : {types_count.get('Nhân', 0)} dòng")
        print(f"Loại mẫu Bộ                    : {types_count.get('Bộ', 0)} dòng")
        print(f"Không làm mẫu / Chưa rõ        : {types_count.get(None, 0)} dòng")
        print("-" * 60)
        print(f"Trạng thái Đã Ký (DK)          : {status_count.get('DK', 0)} dòng")
        print(f"Trạng thái Hoàn Thành (HT)     : {status_count.get('HT', 0)} dòng")
        print(f"Trạng thái Đang Làm (ĐL)       : {status_count.get('ĐL', 0)} dòng")
        print(f"Trạng thái Chưa rõ / Trống     : {status_count.get(None, 0)} dòng")

        print("\n" + "="*60)
        print("SỐ DÒNG ĐỌC ĐƯỢC TỪ MỖI FILE ĐẦU VÀO:")
        print("="*60)
        print(f"File 1 (Chính - Mến)    [TUẦN 39 (21.09-27.09).xlsx]: {count_f1} dòng")
        print(f"File 2 (Khách lẻ - Thu) [KQM TUẦN 39.xlsx]           : {count_f2} dòng")
        print(f"File 3 (Nhật ký - Lan)  [W39.xlsx]                   : {count_f3} dòng")
        print("="*60)
        print("\nHOÀN TẤT")
        print("Đường dẫn tuyệt đối của từng sản phẩm:")
        for fpath in generated_files:
            print(f"  * {os.path.abspath(fpath)}")

if __name__ == "__main__":
    cleaner = DataCleanerAndMergerT39()
    cleaner.run()
