import os
import shutil
import re
import unicodedata
import logging
import csv
from PIL import Image, ExifTags
import piexif

# ----------------------------------------------------------------------------------------------------------------------
# Configuration
# ----------------------------------------------------------------------------------------------------------------------

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

# Define file paths (Update these paths as needed)
folder_path = r'C:\Users\anhb3\Desktop\long\test\Thời trang quyến rũ'       # For Image Processing
html_folder_path = r'C:\Users\anhb3\Desktop\long\test\Thời trang quyến rũ'            # For HTML Processing
csv_file_path = r'C:\Users\anhb3\Desktop\long\chen_anh.csv'                 # CSV for Image HTML
links_file_path = r'C:\Users\anhb3\Desktop\long\di_link.txt'                # TXT for Links

# ----------------------------------------------------------------------------------------------------------------------
# Image Processing Functions
# ----------------------------------------------------------------------------------------------------------------------

def is_image_readable(file_path):
    """
    Check if an image file is readable.

    Args:
        file_path (str): Path to the image file.

    Returns:
        bool: True if the image is readable, False otherwise.
    """
    try:
        with Image.open(file_path) as img:
            img.verify()
        return True
    except Exception:
        return False

def delete_unreadable_images(folder_path):
    """
    Delete unreadable image files within a folder.

    Args:
        folder_path (str): Path to the folder to scan.
    """
    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')
    deleted_files = 0
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith(supported_formats):
                file_path = os.path.join(root, file)
                if not is_image_readable(file_path):
                    try:
                        os.remove(file_path)
                        logging.info(f"Deleted unreadable image: {file_path}")
                        deleted_files += 1
                    except Exception as e:
                        logging.error(f"Error deleting {file_path}: {e}")
    logging.info(f"Total unreadable image files deleted: {deleted_files}")

def count_image_files(folder_path, extensions=('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')):
    """
    Count the number of image files in a folder.

    Args:
        folder_path (str): Path to the folder.
        extensions (tuple): Tuple of file extensions to count.

    Returns:
        int: Number of image files.
    """
    return len([f for f in os.listdir(folder_path) if f.lower().endswith(extensions)])

def delete_subfolders_with_few_images(folder_path, threshold=3):
    """
    Delete subfolders containing a number of image files less than or equal to a threshold.

    Args:
        folder_path (str): Path to the parent folder.
        threshold (int): Threshold number of images. Default is 3.
    """
    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')
    deleted_folders = 0
    for root, dirs, _ in os.walk(folder_path, topdown=False):
        for dir_name in dirs:
            subfolder_path = os.path.join(root, dir_name)
            try:
                image_count = count_image_files(subfolder_path, extensions=supported_formats)
                if image_count <= threshold:
                    shutil.rmtree(subfolder_path)
                    logging.info(f"Deleted subfolder: {subfolder_path} (contained {image_count} image files)")
                    deleted_folders += 1
            except Exception as e:
                logging.error(f"Error processing subfolder {subfolder_path}: {e}")
    logging.info(f"Total subfolders deleted: {deleted_folders}")

def trim_image_files(folder_path, target_count=4):
    """
    Trim the number of image files in subfolders to a target count.

    Args:
        folder_path (str): Path to the parent folder.
        target_count (int): Desired maximum number of images per subfolder. Default is 4.
    """

    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')

    # Improved sorting logic to prioritize lower indices
    def file_sort_key(x):
        """
        Extract the numerical index from the filename for sorting.

        Args:
            x (str): Filename.

        Returns:
            int: Extracted index or a large number if not found.
        """
        match = re.search(r'_(\d+).(jpg|jpeg|png|bmp|gif|tiff|webp)$', x, re.IGNORECASE)
        if match:
            return int(match.group(1))
        else:
            # If no index is found, assign a large number to push it to the end
            return float('inf')

    for root, _, files in os.walk(folder_path):
        image_files = [f for f in files if f.lower().endswith(supported_formats)]
        image_count = len(image_files)

        if image_count <= target_count:
            continue

        # Sort images in ascending order based on their numerical index
        image_files.sort(key=file_sort_key)

        # Select files to delete beyond the target count
        files_to_delete = image_files[target_count:]

        for file in files_to_delete:
            file_path = os.path.join(root, file)
            try:
                os.remove(file_path)
                logging.info(f"Deleted excess image: {file_path}")
            except Exception as e:
                logging.error(f"Error deleting {file_path}: {e}")

def convert_jpg_to_rgb(folder_path):
    """
    Convert all .jpg and .jpeg images to RGB mode.

    Args:
        folder_path (str): Path to the folder containing images.
    """
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith(('.jpg', '.jpeg')):
                file_path = os.path.join(root, file)
                try:
                    with Image.open(file_path) as img:
                        # Apply EXIF orientation to ensure correct dimensions
                        img = apply_exif_orientation(img)

                        # Check if image is not in RGB mode
                        if img.mode != 'RGB':
                            img = img.convert('RGB')
                            # Save the image back to the same file
                            img.save(file_path, format='JPEG', quality=85)
                            logging.info(f"Converted image to RGB: {file_path}")
                        else:
                            logging.debug(f"Image already in RGB mode: {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def is_image_all_white(img):
    """
    Check if an image is entirely white.

    Args:
        img (PIL.Image.Image): The image to check.

    Returns:
        bool: True if the image is all white, False otherwise.
    """
    try:
        if img.mode != 'RGB':
            img = img.convert('RGB')
        extrema = img.getextrema()
        # Extrema is a list where each item is a tuple of (min, max) for each channel
        # For white image, all channels should have min and max as 255
        return all(channel_extremum == (255, 255) for channel_extremum in extrema)
    except Exception as e:
        logging.error(f"Error checking if image is all white: {e}")
        return False

def delete_blank_images(folder_path):
    """
    Delete images that are entirely white.

    Args:
        folder_path (str): Path to the folder to scan.
    """
    supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp')
    deleted_files = 0
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith(supported_formats):
                file_path = os.path.join(root, file)
                try:
                    with Image.open(file_path) as img:
                        img = apply_exif_orientation(img)
                        if is_image_all_white(img):
                            os.remove(file_path)
                            logging.info(f"Deleted blank (all white) image: {file_path}")
                            deleted_files += 1
                except Exception as e:
                    logging.error(f"Error processing {file_path} for blank check: {e}")
    logging.info(f"Total blank images deleted: {deleted_files}")

def convert_and_resize_images(folder_path, target_width=1200):
    """
    Convert all images to JPG and resize them to a target width while maintaining aspect ratio.

    Args:
        folder_path (str): Path to the folder containing images.
        target_width (int): The target width for the resized images. Default is 1200 pixels.
    """
    # Define supported image formats excluding '.jpg' and '.jpeg'
    supported_formats = ('.png', '.bmp', '.gif', '.tiff', '.webp')

    for root, _, files in os.walk(folder_path):
        for file in files:
            file_lower = file.lower()
            file_path = os.path.join(root, file)

            try:
                # Handle .jpg and .jpeg files
                if file_lower.endswith(('.jpg', '.jpeg')):
                    with Image.open(file_path) as img:
                        img = apply_exif_orientation(img)

                        # Resize if necessary (regardless of size)
                        if img.width != target_width:
                            ratio = target_width / float(img.width)
                            new_size = (target_width, int(img.height * ratio))
                            img = img.resize(new_size, Image.LANCZOS)
                            img.save(file_path, format='JPEG', quality=85)
                            logging.info(f"Resized image: {file_path}")
                        else:
                            logging.info(f"Image already at target size: {file_path}")

                # Handle other supported formats
                elif file_lower.endswith(supported_formats):
                    with Image.open(file_path) as img:
                        img = apply_exif_orientation(img)

                        # Convert to RGB if necessary
                        if img.mode in ("RGBA", "P", "LA"):
                            img = img.convert("RGB")
                            logging.debug(f"Converted image mode to RGB: {file_path}")

                        # Resize if necessary (regardless of size)
                        if img.width != target_width:
                            ratio = target_width / float(img.width)
                            new_size = (target_width, int(img.height * ratio))
                            img = img.resize(new_size, Image.LANCZOS)
                            logging.info(f"Resized image: {file_path}")

                        # Save as JPG
                        new_file_path = os.path.splitext(file_path)[0] + '.jpg'
                        img.save(new_file_path, 'JPEG', quality=85)
                        logging.info(f"Converted and saved image as JPG: {new_file_path}")

                    # Remove the original file
                    os.remove(file_path)
                    logging.info(f"Removed original image: {file_path}")

                else:
                    logging.debug(f"Unsupported file format, skipping: {file_path}")

            except Exception as e:
                logging.error(f"Error processing {file_path}: {e}")

def apply_exif_orientation(img):
    """
    Apply EXIF orientation to the image to ensure correct dimensions.

    Args:
        img (PIL.Image.Image): The image to be oriented.

    Returns:
        PIL.Image.Image: The oriented image.
    """
    try:
        exif = img._getexif()
        if exif is not None:
            for key, value in exif.items():
                tag = ExifTags.TAGS.get(key, key)
                if tag == 'Orientation':
                    orientation = value
                    break
            else:
                orientation = None

            if orientation == 3:
                img = img.rotate(180, expand=True)
                logging.debug("Applied EXIF orientation: rotated 180 degrees")
            elif orientation == 6:
                img = img.rotate(270, expand=True)
                logging.debug("Applied EXIF orientation: rotated 270 degrees")
            elif orientation == 8:
                img = img.rotate(90, expand=True)
                logging.debug("Applied EXIF orientation: rotated 90 degrees")
    except Exception as e:
        logging.warning(f"Could not apply EXIF orientation: {e}")
    return img


def rename_jpg_files(image_folder_path):
    """
    Rename .jpg files in each subfolder based on the subfolder name.

    Args:
        image_folder_path (str): Path to the parent folder containing subfolders with images.
    """

    def sanitize_filename(name):
        """
        Sanitize the folder name by replacing specific characters based on a mapping,
        converting to lowercase, and replacing spaces and special characters with hyphens.

        Args:
            name (str): Original folder name.

        Returns:
            str: Sanitized folder name.
        """
        # Define the character mapping based on the provided original and result lists
        char_map = {
            # Lowercase letters and their mappings
            'a': 'a', 'à': 'a', 'á': 'a', 'ả': 'a', 'ã': 'a', 'ạ': 'a',
            'ă': 'a', 'ằ': 'a', 'ắ': 'a', 'ẳ': 'a', 'ẵ': 'a', 'ặ': 'a',
            'â': 'a', 'ấ': 'a', 'ầ': 'a', 'ẩ': 'a', 'ẫ': 'a', 'ậ': 'a',

            # 'b' to 'c'
            'b': 'b', 'c': 'c',

            # 'd' mappings
            'd': 'd', 'đ': 'd',

            # 'e' mappings
            'e': 'e', 'é': 'e', 'è': 'e', 'ẻ': 'e', 'ẽ': 'e', 'ẹ': 'e',
            'ê': 'e', 'ế': 'e', 'ề': 'e', 'ể': 'e', 'ễ': 'e', 'ệ': 'e',

            # 'g' to 'h'
            'g': 'g', 'h': 'h',

            # 'i' mappings
            'i': 'i', 'í': 'i', 'ì': 'i', 'ỉ': 'i', 'ĩ': 'i', 'ị': 'i',

            # 'k' to 'm'
            'k': 'k', 'l': 'l', 'm': 'm',

            # 'n' mapping
            'n': 'n',

            # 'o' mappings
            'o': 'o', 'ó': 'o', 'ò': 'o', 'ỏ': 'o', 'õ': 'o', 'ọ': 'o',
            'ô': 'o', 'ố': 'o', 'ồ': 'o', 'ổ': 'o', 'ỗ': 'o', 'ộ': 'o',
            'ơ': 'o', 'ớ': 'o', 'ờ': 'o', 'ở': 'o', 'ỡ': 'o', 'ợ': 'o',

            # 'p' to 's'
            'p': 'p', 'q': 'q', 'r': 'r', 's': 's',

            # 't' mapping
            't': 't',

            # 'u' mappings
            'u': 'u', 'ú': 'u', 'ù': 'u', 'ủ': 'u', 'ũ': 'u', 'ụ': 'u',
            'ư': 'u', 'ứ': 'u', 'ừ': 'u', 'ử': 'u', 'ữ': 'u', 'ự': 'u',

            # 'v' to 'z'
            'v': 'v', 'x': 'x', 'y': 'y', 'z': 'z',

            # Special characters and their replacements
            # ' ': '-', '-': '-', '_': '-', '.': '-', ',': '-', '/': '-', '\': ' - ', ' & ': ' and ',

            # Numbers
                                                                                                 '0': '0', '1': '1',
            '2': '2', '3': '3', '4': '4',
            '5': '5', '6': '6', '7': '7', '8': '8', '9': '9',

            # Uppercase letters (convert to lowercase)
            **{c.upper(): v for c, v in {
                # Include uppercase mappings
                'a': 'a', 'à': 'a', 'á': 'a', 'ả': 'a', 'ã': 'a', 'ạ': 'a',
                'ă': 'a', 'ằ': 'a', 'ắ': 'a', 'ẳ': 'a', 'ẵ': 'a', 'ặ': 'a',
                'â': 'a', 'ấ': 'a', 'ầ': 'a', 'ẩ': 'a', 'ẫ': 'a', 'ậ': 'a',
                'b': 'b', 'c': 'c',
                'd': 'd', 'đ': 'd',
                'e': 'e', 'é': 'e', 'è': 'e', 'ẻ': 'e', 'ẽ': 'e', 'ẹ': 'e',
                'ê': 'e', 'ế': 'e', 'ề': 'e', 'ể': 'e', 'ễ': 'e', 'ệ': 'e',
                'g': 'g', 'h': 'h',
                'i': 'i', 'í': 'i', 'ì': 'i', 'ỉ': 'i', 'ĩ': 'i', 'ị': 'i',
                'k': 'k', 'l': 'l', 'm': 'm',
                'n': 'n',
                'o': 'o', 'ó': 'o', 'ò': 'o', 'ỏ': 'o', 'õ': 'o', 'ọ': 'o',
                'ô': 'o', 'ố': 'o', 'ồ': 'o', 'ổ': 'o', 'ỗ': 'o', 'ộ': 'o',
                'ơ': 'o', 'ớ': 'o', 'ờ': 'o', 'ở': 'o', 'ỡ': 'o', 'ợ': 'o',
                'p': 'p', 'q': 'q', 'r': 'r', 's': 's',
                't': 't',
                'u': 'u', 'ú': 'u', 'ù': 'u', 'ủ': 'u', 'ũ': 'u', 'ụ': 'u',
                'ư': 'u', 'ứ': 'u', 'ừ': 'u', 'ử': 'u', 'ữ': 'u', 'ự': 'u',
                'v': 'v', 'x': 'x', 'y': 'y', 'z': 'z'
            }.items()}
        }

        sanitized = ''
        for char in name:
            if char in char_map:
                sanitized += char_map[char]
            elif char.isalnum():
                # Keep alphanumeric characters not in char_map
                sanitized += char.lower()
            else:
                # Replace any other character with a hyphen
                sanitized += '-'

        # Replace multiple consecutive hyphens with a single hyphen
        sanitized = re.sub(r'-+', '-', sanitized)

        # Remove leading and trailing hyphens
        sanitized = sanitized.strip('-')

        return sanitized

    for root, dirs, files in os.walk(image_folder_path):
        for dir_name in dirs:
            subfolder_path = os.path.join(root, dir_name)
            sanitized_base = sanitize_filename(dir_name)

            if not sanitized_base:
                logging.warning(f"Subfolder '{dir_name}' results in an empty base name after sanitization. Skipping...")
                continue

            # Gather all .jpg and .jpeg files
            jpg_files = [f for f in os.listdir(subfolder_path) if f.lower().endswith(('.jpg', '.jpeg'))]

            # Sort files to ensure consistent renaming order
            jpg_files.sort()

            for index, filename in enumerate(jpg_files, start=1):
                original_file_path = os.path.join(subfolder_path, filename)

                # Define the new filename
                new_filename = f"{sanitized_base}-{index}.jpg"
                new_file_path = os.path.join(subfolder_path, new_filename)

                # Check if the new filename already exists to avoid overwriting
                if os.path.exists(new_file_path):
                    logging.warning(
                        f"Cannot rename '{original_file_path}' to '{new_file_path}': Target file already exists.")
                    continue

                try:
                    os.rename(original_file_path, new_file_path)
                    logging.info(f"Renamed '{original_file_path}' to '{new_file_path}'")
                except Exception as e:
                    logging.error(f"Error renaming '{original_file_path}' to '{new_file_path}': {e}")

def add_metadata_to_images(folder_path):
    """
    Add metadata (Title, Subject, Tags, Comments) to .jpg images based on their subfolder's name.

    Args:
        folder_path (str): Path to the parent folder containing subfolders with images.
    """
    supported_formats = ('.jpg', '.jpeg')

    for root, dirs, files in os.walk(folder_path):
        for dir_name in dirs:
            subfolder_path = os.path.join(root, dir_name)
            metadata_value = dir_name  # Using subfolder's name as the metadata value

            # Iterate through each .jpg file in the subfolder
            for file in os.listdir(subfolder_path):
                if file.lower().endswith(supported_formats):
                    file_path = os.path.join(subfolder_path, file)
                    try:
                        # Load existing EXIF data
                        exif_dict = {}
                        if os.path.exists(file_path):
                            try:
                                exif_dict = piexif.load(file_path)
                            except piexif.InvalidImageDataError:
                                exif_dict = {"0th": {}, "Exif": {}, "GPS": {}, "1st": {}, "thumbnail": None}

                        # Define the metadata fields
                        title = metadata_value
                        subject = metadata_value
                        tags = metadata_value
                        comments = metadata_value

                        # Update ImageDescription (0th IFD)
                        exif_dict["0th"][piexif.ImageIFD.ImageDescription] = title.encode('utf-8')

                        # Update XPSubject (0th IFD) - Requires UTF-16LE encoding
                        exif_dict["0th"][piexif.ImageIFD.XPSubject] = subject.encode('utf-16le')

                        # Update XPKeywords (0th IFD) - Requires UTF-16LE encoding
                        exif_dict["0th"][piexif.ImageIFD.XPKeywords] = tags.encode('utf-16le')

                        # Update UserComment (Exif IFD)
                        user_comment_prefix = b'ASCII\x00\x00\x00'  # ASCII encoding prefix
                        user_comment = user_comment_prefix + comments.encode('utf-8')
                        exif_dict["Exif"][piexif.ExifIFD.UserComment] = user_comment

                        # Dump the updated EXIF data to bytes
                        exif_bytes = piexif.dump(exif_dict)

                        # Save the image with updated EXIF data
                        piexif.insert(exif_bytes, file_path)

                        logging.info(f"Added metadata to image: {file_path}")

                    except Exception as e:
                        logging.error(f"Error adding metadata to {file_path}: {e}")

# ----------------------------------------------------------------------------------------------------------------------
# HTML Processing Functions
# ----------------------------------------------------------------------------------------------------------------------

def delete_small_html_folders(folder_path, min_char_count=6000):
    """
    Delete folders containing .html files with content less than a minimum character count.

    Args:
        folder_path (str): Path to the parent folder.
        min_char_count (int): Minimum character count required. Default is 6000.
    """
    for dirpath, _, filenames in os.walk(folder_path, topdown=False):
        delete_folder = False

        for filename in filenames:
            if filename.lower().endswith('.html'):
                file_path = os.path.join(dirpath, filename)
                try:
                    with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
                        content = file.read()
                    if len(content) < min_char_count:
                        delete_folder = True
                        break
                except Exception as e:
                    logging.error(f"Error reading {file_path}: {e}")

        if delete_folder:
            try:
                shutil.rmtree(dirpath)
                logging.info(f"Deleted folder due to small HTML content: {dirpath}")
            except Exception as e:
                logging.error(f"Error deleting folder {dirpath}: {e}")

def remove_blank_lines(file_path):
    """
    Remove blank lines from a file.

    Args:
        file_path (str): Path to the file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            lines = file.readlines()

        non_blank_lines = [line.rstrip() for line in lines if line.strip()]

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write('\n'.join(non_blank_lines))
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def remove_img_tags(file_path):
    """
    Remove all <img> tags from an HTML file.

    Args:
        file_path (str): Path to the HTML file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        # Remove img tags using regex
        cleaned_content = re.sub(r'<img\b[^>]>', '', content, flags=re.IGNORECASE)

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(cleaned_content)
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def replace_h1_tag(file_path):
    """
    Replace <h1 style="text-align: center;"> with <h1> in an HTML file.

    Args:
        file_path (str): Path to the HTML file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        modified_content = content.replace('<h1 style="text-align: center;">', '<h1>')

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(modified_content)
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def capitalize_first_letter_in_tags(content):
    """
    Capitalize the first letter of text inside HTML tags.

    Args:
        content (str): The HTML content as a string.

    Returns:
        str: Modified content with capitalized text.
    """
    # Function to capitalize text inside tags
    def capitalize_match(match):
        tag = match.group(1)
        text = match.group(2).strip()
        if text:
            return f"<{tag}>{text[0].upper()}{text[1:]}</{tag}>"
        else:
            return match.group(0)

    # Regex pattern to match tags and their content
    pattern = r'<([a-z0-9]+)(?:\s+[^>])?>([^<]+)</\1>'
    modified_content = re.sub(pattern, capitalize_match, content, flags=re.IGNORECASE)
    return modified_content

def combine_html_tags_into_same_line(content):
    """
    Combine specific HTML tags into the same line by removing unnecessary line breaks.

    Args:
    content (str): The HTML content as a string.

    Returns:
    str: Modified content with specific tags combined into the same line.
    """
    # Replace '>' followed by any whitespace and newline followed by '<' with '> <'
    content = re.sub(r'>\s*\n\s*<', '> <', content)
    return content

def combine_blockquote_lines_in_content(content):
    """
    Combine broken <blockquote> lines in HTML content.

    Args:
        content (str): HTML content as a string.

    Returns:
        str: Modified content with combined blockquotes.
    """
    lines = content.splitlines()
    combined_lines = []
    skip_next = False

    for i, line in enumerate(lines):
        if skip_next:
            skip_next = False
            continue

        line_stripped = line.strip()

        # Check for opening <blockquote> without closing tag
        if '<blockquote>' in line_stripped and '</blockquote>' not in line_stripped:
            if i + 1 < len(lines):
                next_line = lines[i + 1].strip()
                combined_line = line_stripped + ' ' + next_line
                combined_lines.append(combined_line)
                skip_next = True
            else:
                combined_lines.append(line_stripped)
        # Check for closing </blockquote> without opening tag
        elif '</blockquote>' in line_stripped and '<blockquote>' not in line_stripped:
            if combined_lines:
                combined_lines[-1] += ' ' + line_stripped
            else:
                combined_lines.append(line_stripped)
        else:
            combined_lines.append(line_stripped)

    return '\n'.join(combined_lines)

def process_html_file(file_path):
    """
    Apply processing functions to an HTML file.

    Args:
        file_path (str): Path to the HTML file.
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()

        # Remove <img> tags
        content = re.sub(r'<img\b[^>]>', '', content, flags=re.IGNORECASE)

        # Replace <h1 style="text-align: center;"> with <h1>
        content = content.replace('<h1 style="text-align: center;">', '<h1>')

        # Capitalize first letter inside tags
        content = capitalize_first_letter_in_tags(content)

        # Combine <blockquote> lines
        content = combine_blockquote_lines_in_content(content)

        # Remove blank lines
        content = '\n'.join([line for line in content.splitlines() if line.strip()])

        # Combine specific HTML tags into the same line
        # content = combine_html_tags_into_same_line(content)

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(content)

        logging.info(f"Processed HTML file: {file_path}")
    except Exception as e:
        logging.error(f"Error processing {file_path}: {e}")

def read_image_html_from_csv(csv_file_path):
    """
    Read image HTML snippets from a CSV file.

    Args:
        csv_file_path (str): Path to the CSV file.

    Returns:
        dict: Dictionary mapping folder names to lists of image HTML lines.
    """
    image_html_dict = {}
    try:
        with open(csv_file_path, 'r', encoding='utf-8') as file:
            csv_reader = csv.reader(file)
            for row in csv_reader:
                if len(row) == 2:
                    folder_name = row[0].strip()
                    if folder_name not in image_html_dict:
                        image_html_dict[folder_name] = []
                    image_html_dict[folder_name].append(row[1].strip())
    except Exception as e:
        logging.error(f"Error reading CSV file {csv_file_path}: {e}")
    return image_html_dict

def read_links_from_txt(txt_file_path):
    """
    Read links from a TXT file.

    Args:
        txt_file_path (str): Path to the TXT file.

    Returns:
        list: List of links.
    """
    try:
        with open(txt_file_path, 'r', encoding='utf-8') as file:
            return [line.strip() for line in file if line.strip()]
    except Exception as e:
        logging.error(f"Error reading TXT file {txt_file_path}: {e}")
        return []

def insert_image_html(html_content, image_html_lines):
    """
    Insert image HTML lines into the HTML content at specified positions.

    Args:
        html_content (str): Original HTML content.
        image_html_lines (list): List of image HTML lines to insert.

    Returns:
        str: Modified HTML content with images inserted.
    """
    content_length = len(html_content)
    insertion_points = [
        int(content_length * 0.125),
        int(content_length * 0.375),
        int(content_length * 0.625),
        int(content_length * 0.875)
    ]

    for i, point in enumerate(insertion_points):
        start_of_line = html_content.rfind('\n', 0, point)
        end_of_line = html_content.find('\n', point)

        if start_of_line == -1:
            start_of_line = 0
        if end_of_line == -1:
            end_of_line = content_length

        if point - start_of_line <= end_of_line - point:
            insertion_index = start_of_line + 1
        else:
            insertion_index = end_of_line + 1

        if i < len(image_html_lines):
            html_content = html_content[:insertion_index] + image_html_lines[i] + '\n' + html_content[insertion_index:]

    return html_content

def insert_link_at_end(html_content, link):
    """
    Insert a link at the end of the "Xem thêm: " section in the HTML content.

    Args:
        html_content (str): Original HTML content.
        link (str): The link to insert.

    Returns:
        str: Modified HTML content with the link inserted.
    """
    last_xem_them = html_content.rfind("Xem thêm: ")
    if last_xem_them != -1:
        insertion_point = last_xem_them + len("Xem thêm: ")
        return html_content[:insertion_point] + link + html_content[insertion_point:]
    else:
        return html_content.rstrip() + "\nXem thêm: " + link

def insert_formatted_title(html_content):
    """
    Insert a formatted title after the first </blockquote> tag.

    Args:
        html_content (str): Original HTML content.

    Returns:
        str: Modified HTML content with the formatted title inserted.
    """
    # Find the title
    title_match = re.search(r'<h1\b[^>]><strong>(.?)</strong></h1>', html_content, re.IGNORECASE)
    if not title_match:
        return html_content  # No title found, return unchanged content

    title = title_match.group(1)

    # Prepare the formatted title
    if '?' in title:
        formatted_title = f'<h2><strong>{title} Bài viết dưới đây sẽ giải thích cho bạn:</strong></h2>'
    else:
        formatted_title = f'<h2><strong>{title} là gì? Bài viết dưới đây sẽ giải thích cho bạn:</strong></h2>'

    # Find the first </blockquote> and insert the formatted title after it
    blockquote_end = html_content.find('</blockquote>')
    if blockquote_end != -1:
        # Find the end of the line containing </blockquote>
        end_of_line = html_content.find('\n', blockquote_end)
        if end_of_line == -1:
            end_of_line = len(html_content)
        insertion_point = end_of_line + 1
        return html_content[:insertion_point] + formatted_title + '\n' + html_content[insertion_point:]
    else:
        return html_content  # No </blockquote> found, return unchanged content

def insert_title_in_blockquote(html_content):
    """
    Insert the title followed by " - " immediately after the opening <blockquote> tag.

    Args:
        html_content (str): Original HTML content.

    Returns:
        str: Modified HTML content with the title inserted in blockquote.
    """
    # Find the title
    title_match = re.search(r'<h1\b[^>]><strong>(.*?)</strong></h1>', html_content, re.IGNORECASE)
    if not title_match:
        return html_content  # No title found, return unchanged content

    title = title_match.group(1)

    # Find the first <blockquote> tag
    blockquote_start = html_content.find('<blockquote>')
    if blockquote_start == -1:
        return html_content  # No <blockquote> found, return unchanged content

    # Insert the title followed by " - " just after the opening <blockquote> tag
    insertion_point = blockquote_start + len('<blockquote>')
    return html_content[:insertion_point] + f'{title} - ' + html_content[insertion_point:]

def remove_h1_strong_tags(folder_path):
    """
    Remove <h1><strong> and </strong></h1> tags from the first line of HTML files.

    Args:
        folder_path (str): Path to the folder.
    """
    for subdir, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(subdir, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as file_obj:
                        lines = file_obj.readlines()

                    if lines:
                        lines[0] = lines[0].replace('<h1><strong>', '').replace('</strong></h1>', '')

                    with open(file_path, 'w', encoding='utf-8') as file_obj:
                        file_obj.writelines(lines)

                    logging.info(f"Removed <h1><strong> tags in: {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def add_xem_them_to_html(folder_path):
    """
    Add the string "Xem thêm: " at the end of each HTML file.

    Args:
        folder_path (str): Path to the folder.
    """
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(root, file)
                try:
                    # Ensure we append to the file
                    with open(file_path, 'a', encoding='utf-8') as f:
                        f.write('\n\nXem thêm: ')
                    logging.info(f"Added 'Xem thêm: ' to {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def remove_quotes_from_html(folder_path):
    """
    Remove all quotation marks from HTML files.

    Args:
        folder_path (str): Path to the folder.
    """
    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()

                    # Remove all quotation marks
                    content = content.replace('"', '').replace('“', '').replace('”', '')

                    with open(file_path, 'w', encoding='utf-8') as f:
                        f.write(content)

                    logging.info(f"Removed quotation marks from: {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def capitalize_after_colon_in_html(folder_path):
    """
    Capitalize the first letter after a colon in HTML files.

    Args:
        folder_path (str): Path to the folder.
    """
    pattern = re.compile(r'(: )([a-z])')

    for root, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()

                    # Capitalize first letter after ": "
                    modified_content = pattern.sub(lambda m: m.group(1) + m.group(2).upper(), content)

                    if content != modified_content:
                        with open(file_path, 'w', encoding='utf-8') as f:
                            f.write(modified_content)
                        logging.info(f"Capitalized text after colon in: {file_path}")
                except Exception as e:
                    logging.error(f"Error processing {file_path}: {e}")

def process_html_files(folder_path):
    """
    Process all HTML files in a folder, applying several cleaning functions.

    Args:
        folder_path (str): Path to the folder.
    """
    for subdir, _, files in os.walk(folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                file_path = os.path.join(subdir, file)
                process_html_file(file_path)

# ----------------------------------------------------------------------------------------------------------------------
# Script 2: Image and Link Insertion into HTML
# ----------------------------------------------------------------------------------------------------------------------

def insert_image_and_links_into_html(html_folder_path, csv_file_path, links_file_path):
    """
    Insert image HTML snippets and links into HTML files based on CSV and TXT inputs.

    Args:
        html_folder_path (str): Path to the folder containing HTML files.
        csv_file_path (str): Path to the CSV file containing image HTML snippets.
        links_file_path (str): Path to the TXT file containing links.
    """
    image_html_dict = read_image_html_from_csv(csv_file_path)
    links = read_links_from_txt(links_file_path)
    link_index = 0

    all_html_files = []
    for root, dirs, files in os.walk(html_folder_path):
        for file in files:
            if file.lower().endswith('.html'):
                all_html_files.append(os.path.join(root, file))
    all_html_files.sort()

    for file_path in all_html_files:
        folder_name = os.path.basename(os.path.dirname(file_path))

        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                html_content = f.read()

            # Insert title in blockquote
            html_content = insert_title_in_blockquote(html_content)

            # Insert formatted title
            html_content = insert_formatted_title(html_content)

            # Insert image HTML if the folder name matches
            if folder_name in image_html_dict:
                html_content = insert_image_html(html_content, image_html_dict[folder_name])

            # Insert link at the end
            if link_index < len(links):
                html_content = insert_link_at_end(html_content, links[link_index])
                link_index += 1

            # Save the modified content
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(html_content)

            # Reset link index if we've used all links
            if link_index >= len(links):
                link_index = 0

            logging.info(f"Inserted images and links into: {file_path}")

        except Exception as e:
            logging.error(f"Error processing {file_path}: {e}")

# ----------------------------------------------------------------------------------------------------------------------
# Main Execution Flow
# ----------------------------------------------------------------------------------------------------------------------

def main(image_folder_path, html_folder_path, csv_file_path, links_file_path):
    """
    Main function to execute all processing steps.

    Args:
        image_folder_path (str): Path to the folder to process images.
        html_folder_path (str): Path to the folder to process HTML files.
        csv_file_path (str): Path to the CSV file for image HTML snippets.
        links_file_path (str): Path to the TXT file containing links.
    """

    # ------------------------------
    # Image Processing
    # ------------------------------
    logging.info("Starting image processing...")

    # Step 1: Delete unreadable image files
    delete_unreadable_images(image_folder_path)

    # Step 2: Delete blank (all white) images
    delete_blank_images(image_folder_path)

    # Step 3: Delete subfolders with few images
    delete_subfolders_with_few_images(image_folder_path, threshold=3)

    # Step 4: Trim image files in remaining subfolders
    trim_image_files(image_folder_path, target_count=4)

    # Step 5: Convert all .jpg images to RGB mode
    convert_jpg_to_rgb(image_folder_path)

    # Step 6: Convert and resize images
    convert_and_resize_images(image_folder_path, target_width=1200)

    # Step 7: Rename .jpg files based on subfolder names
    rename_jpg_files(image_folder_path)

    # Step 8: Add metadata to images based on subfolder names
    add_metadata_to_images(image_folder_path)

    logging.info("Image processing completed.")

    # ------------------------------
    # HTML Processing
    # ------------------------------
    logging.info("Starting HTML processing...")

    # Step 6: Delete small HTML folders
    delete_small_html_folders(html_folder_path, min_char_count=6000)

    # Step 7: Process HTML files (cleaning)
    process_html_files(html_folder_path)

    # Step 8: Additional HTML modifications
    capitalize_after_colon_in_html(html_folder_path)
    # Uncomment the following lines if needed
    # add_xem_them_to_html(html_folder_path)
    # remove_h1_strong_tags(html_folder_path)
    # remove_quotes_from_html(html_folder_path)

    # Step 9: Insert images and links into HTML files
    insert_image_and_links_into_html(html_folder_path, csv_file_path, links_file_path)

    logging.info("HTML processing completed.")

if __name__ == "__main__":
    main(folder_path, html_folder_path, csv_file_path, links_file_path)