from io import BytesIO
import base64
import qrcode
from PIL import Image, ImageDraw, ImageFont
import os
import string
import re
import colorsys

# --- Configuration & Styling ---
# The target green color (the professional deep green) you selected from your previous step.

colors = [
    (0, 104, 55),   
    (122, 191, 66),  
    (0, 51, 102),   
    (70, 130, 180),     
    (85, 107, 47)    
]

color_map = {letter: colors[i] for i, letter in enumerate(string.ascii_lowercase[:len(colors)])}


FONT_PATH_REGULAR = "fonts/Arial.ttf"
FONT_PATH_BOLD = "fonts/Arialbd.ttf"

# def get_color(letter):
#     letter = letter.lower()

#     if letter not in color_map:
#         raise ValueError(f"Invalid color key '{letter}'. Available keys: {list(color_map.keys())}")

#     return color_map[letter]

# --- Helper Function: Advanced Color Change ---
# This version handles the smooth edges of the green triangles better than a simple threshold.
def create_recolored_template(input_img_path, output_img_path, target_color):
    """
    Creates a clean recolored base by replacing specific neon green tones.
    """
    print("Recoloring template...")
    img = Image.open(input_img_path).convert("RGB")
    pixels = img.load()

    # The original neon green is around (57, 255, 20).
    # This broader condition catches all variations and gradients in the triangles.
    for x in range(img.width):
        for y in range(img.height):
            r, g, b = pixels[x, y]
            
            # BROAD DETECTION: Green is dominant and significantly brighter than red/blue.
            # This handles anti-aliasing edges perfectly.
            if g > 150 and g > r + 80 and g > b + 80:
                pixels[x, y] = target_color

    img.save(output_img_path)
    return output_img_path

# --- Helper Function: Generate Professional QR Code ---
def generate_pass_qr(term, session, pass_no):
    """
    Generates a high-quality QR code containing the full text description of the pass.
    """
    print("Generating dynamic QR code...")
    qr_data = f"{pass_no} - Kamfa Modern College, School Fees Gate Pass, {term} Term ({session})"
    
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_H,  # High error correction (can handle logo overlay if needed later)
        box_size=10,
        border=1,  # Minimal white border around the QR data itself
    )
    qr.add_data(qr_data)
    qr.make(fit=True)
    
    qr_img = qr.make_image(fill_color="black", back_color="white").convert("RGB")
    
    # Optional: Crop excessive internal padding from the generation
    # Depending on your need, you can tweak this. For now, we'll use a larger target size.
    
    return qr_img

# --- Main Processing Loop ---
def process_college_pass(term, session, pass_no, color_index, student_name, class_name):
    input_template = "template.png"
    # Create a temporary recolored base to work with
    # target_color = get_color(color_letter)
    target_color = generate_color(int(color_index))
    recolored_base = create_recolored_template(input_template, "temp_base.png", target_color)
    
    pass_img = Image.open(recolored_base).convert("RGB")
    draw = ImageDraw.Draw(pass_img)
    
    font_main = ImageFont.truetype(FONT_PATH_BOLD, 180)     # "0306"
    font_term = ImageFont.truetype(FONT_PATH_BOLD, 90)     # "2ND"
    font_session = ImageFont.truetype(FONT_PATH_BOLD, 60)  # "2025/2026"
    
    font_x = ImageFont.truetype(FONT_PATH_BOLD, 55)  # "2025/2026"

    print("Filling text data...")
    draw.text((300, 80), student_name, font=font_x, fill="black", anchor="mm")
    draw.text((300, 150), class_name, font=font_x, fill="black", anchor="mm")
    
    # 1. Fill the Blue Pass Number Block (e.g., 0306)
    pass_no_normalized = f"{pass_no:04}"
    pass_no_str = "\u2006".join(f"{pass_no_normalized:04}")   # Ensure exactly 4 digits, padded with 0
    draw.text((550, 850), pass_no_str, font=font_main, fill="white", anchor="mm")

    # 2. Fill the "TERM:" detail (e.g., 2ND)
    term_text = f"{term.lower()}"  # Normalize, e.g., 2nd to 2ND
    draw.text((630, 965), term_text, font=font_term, fill="black")

    # 3. Fill the Red Session Block (e.g., 2025/2026)
    draw.text((670, 1150), session, font=font_session, fill="white", anchor="mm")

    # 4. Generate and place the QR Code
    qr_img = generate_pass_qr(term, session, pass_no_normalized)

    # Dynamic resizing: target the QR code to fit neatly in the gap. 
    target_qr_size = (190, 190)
    resized_qr = qr_img.resize(target_qr_size, Image.Resampling.LANCZOS)
    
    # Calculate Paste Position (Top Left corner for paste())
    paste_x = 450
    paste_y = 1210
    
    pass_img.paste(resized_qr, (paste_x, paste_y))

    # --- Clean up and save ---
    # final_output_name = f"filled_pass_{pass_no:04}.png"
    # pass_img.save(final_output_name)
    
    buffer = BytesIO()
    pass_img.save(buffer, format="PNG")
    img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")

    img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
    
    # Clean up the temporary base file
    os.remove("temp_base.png")

    return img_base64



TARGET_GREEN_ENGLISH = (192, 0, 0)

FONT_PATH_BOLD_ENGLISH = "fonts/Arialbd.ttf"


# --- Helper Function: Recolor Template ---
def create_recolored_template_english(input_img_path, output_img_path, target_color):
    print("Recoloring template...")
    img = Image.open(input_img_path).convert("RGB")
    pixels = img.load()

    for x in range(img.width):
        for y in range(img.height):
            r, g, b = pixels[x, y]

            if g > 150 and g > r + 80 and g > b + 80:
                pixels[x, y] = target_color

    img.save(output_img_path)
    return output_img_path


# --- Helper: Extract and Normalize Receipt Number ---
def normalize_receipt_number_english(receipt_no):
    """
    Formats only the leading number to 4 digits,
    then appends the rest of the original string unchanged.
    
    Example:
    1NC9-11   -> 0001NC9-11
    23ABC     -> 0023ABC
    291XYY    -> 0291XYY
    """

    match = re.match(r"(\d+)(.*)", receipt_no)

    if match:
        number_part = int(match.group(1))
        suffix = match.group(2)  # everything after number
    else:
        return receipt_no  # no number found, return as-is

    return f"{number_part:04}{suffix}"


# --- Main Processing ---
def process_college_pass_english(
    receipt_no,
    name,
    class_name,
    level_name,
    time,
    phone,
    starting,
    ending,
    color_index,
    student_photo_base64=None
):
    input_template = "template_english.png"
    
    target_color = generate_color(int(color_index))

    recolored_base = create_recolored_template_english(
        input_template,
        "temp_base.png",
        target_color
    )

    pass_img = Image.open(recolored_base).convert("RGB")
    draw = ImageDraw.Draw(pass_img)
    

    # --- Normalize receipt number ---
    receipt_number_clean = normalize_receipt_number_english(receipt_no)

    # Spread digits nicely
    receipt_display = "".join(receipt_number_clean)

    # 1. RECEIPT NUMBER
    font_no = ImageFont.truetype(FONT_PATH_BOLD_ENGLISH, 30)
    draw.text((500, 885), receipt_display, font=font_no, fill="black", anchor="mm")

    # 2. NAME
    font_name = ImageFont.truetype(FONT_PATH_BOLD_ENGLISH, 35)
    draw.text((195, 810), name, font=font_name, fill="black")

    # 3. CLASS
    font_class = ImageFont.truetype(FONT_PATH_BOLD_ENGLISH, 50)

    if class_name.lower() in ("normal class", "special class", "private special"):
        position = (230, 250)
    elif class_name.lower() == ("children tutorial class"):
        position = (90, 250)
    elif class_name.lower() == ("personal tutorial"):
        position = (170, 250)
    elif class_name.lower() == ("computer based test") or class_name.lower() == ("computer-based test") :
        position = (120, 250)
    elif class_name.lower() == ("waec/neco"):
        position = (280, 250)
    draw.text(position, class_name.upper(), font=font_class, fill=target_color)

    # Level
    font_level = ImageFont.truetype(FONT_PATH_BOLD_ENGLISH, 30)
    draw.text((180, 930), level_name, font=font_level, fill="black")

    # 4. TIME
    font_time = ImageFont.truetype(FONT_PATH_BOLD_ENGLISH, 30)
    draw.text((540, 925), time, font=font_time, fill="black")

    # 5. PHONE
    font_phone = ImageFont.truetype(FONT_PATH_BOLD_ENGLISH, 35)
    draw.text((250, 985), phone, font=font_phone, fill="black")

    # 6. STARTING DATE
    font_date = ImageFont.truetype(FONT_PATH_BOLD_ENGLISH, 35)
    draw.text((220, 1045), starting, font=font_date, fill="black")

    # 7. ENDING DATE
    draw.text((200, 1100), ending, font=font_date, fill="black")
    
    if student_photo_base64:
        try:
            # Decode base64 to image
            photo_data = base64.b64decode(student_photo_base64)
            photo_img = Image.open(BytesIO(photo_data)).convert("RGBA")
            
            # Resize to a reasonable size (you can adjust)
            photo_size = (400, 440)   # width, height – tweak as needed
            photo_img = photo_img.resize(photo_size, Image.Resampling.LANCZOS)
            
            # Position below "Class" text – adjust X,Y as needed
            # Class text is around (230,250) – place photo below it, e.g., (180, 300)
            paste_x = 235
            paste_y = 345
            pass_img.paste(photo_img, (paste_x, paste_y), photo_img)  # use alpha if available
        except Exception as e:
            print(f"Failed to paste student photo: {e}")

    # --- Save to BytesIO and return base64 ---
    buffer = BytesIO()
    pass_img.save(buffer, format="PNG")
    img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")

    # Clean up the temporary base file
    os.remove("temp_base.png")

    return img_base64


def generate_color(index):
    hue = (index * 0.61803398875) % 1
    r, g, b = colorsys.hsv_to_rgb(hue, 0.6, 0.7)

    return (
        int(r * 255),
        int(g * 255),
        int(b * 255)
    )