#!/usr/bin/env python3

"""
Script to populate an ODS template with data extracted from a source Excel file.

The script performs the following steps:
1. Reads the source Excel file "Production Order (mrp.production).xlsx".
2. Extracts a serial number suffix from column B where a cell contains "FA-".
3. Finds specific component rows (Barebone, GPU, RAM, CPU, SSD, RAID) and copies the value from the cell to the right of each.
4. Loads the ODS template "A4004_v3.0-260617.ods".
5. Writes the extracted values into designated ranges in the template.
6. Saves the modified template as a new ODS file named after the value in cell A2 of the source file.

Dependencies:
- pandas (for reading Excel files)
- openpyxl (Excel engine used by pandas)
- ezodf (for reading and writing ODS files)

Install them via:
    pip install pandas openpyxl ezodf

Usage:
    python populate_template.py
"""

import os
import sys
import pandas as pd
import ezodf


def find_fa_suffix(df: pd.DataFrame) -> str:
    """Search column B (index 1) for a cell containing "FA-" and return the suffix.

    Example cell content: "FA-2604N3" → returns "2604N3".
    """
    # Iterate over column B values
    for idx, val in df.iloc[:, 1].items():
        if isinstance(val, str) and "FA-" in val:
            parts = val.split("FA-")
            if len(parts) > 1:
                suffix = parts[1].strip()
                return suffix
    raise ValueError('No cell containing "FA-" found in column B.')


def find_adjacent_value(df: pd.DataFrame, phrase: str) -> str:
    """Search the entire DataFrame for a cell containing *phrase* and return the value
    from the cell immediately to its right (same row, next column)."""
    for row_idx, row in df.iterrows():
        for col_idx, cell in row.iteritems():
            if isinstance(cell, str) and phrase in cell:
                # Ensure there is a column to the right
                if col_idx + 1 < df.shape[1]:
                    adjacent = df.iat[row_idx, col_idx + 1]
                    # Convert NaN to empty string if needed
                    if pd.isna(adjacent):
                        return ""
                    return str(adjacent)
                else:
                    raise IndexError(f"Cell containing '{phrase}' is at the last column; no adjacent cell to copy.")
    raise ValueError(f"Phrase '{phrase}' not found in the source sheet.")


def set_range(sheet: ezodf.Table, start_row: int, start_col: int, end_row: int, end_col: int, value: str) -> None:
    """Fill a rectangular range (inclusive) in the ODS sheet with *value*.
    Rows and columns are zero‑based indices.
    """
    for r in range(start_row, end_row + 1):
        for c in range(start_col, end_col + 1):
            sheet[r, c].set_value(value)


def main() -> None:
    # File names (relative to the script location / workspace root)
    src_excel = "Production Order (mrp.production).xlsx"
    template_ods = "A4004_v3.0-260617.ods"

    # Load the Excel source file – no header, treat everything as string
    df = pd.read_excel(src_excel, header=None, dtype=str)

    # 1. Serial number suffix (FA-...)
    suffix = find_fa_suffix(df)

    # 2. Component values – copy the cell to the right of each label
    barebone_val = find_adjacent_value(df, "All / Components / Barebone")
    gpu_val = find_adjacent_value(df, "All / Components / GPU")
    ram_val = find_adjacent_value(df, "All / Components / RAM")
    cpu_val = find_adjacent_value(df, "All / Components / CPU")
    ssd_val = find_adjacent_value(df, "All / Components / SSD")
    raid_val = find_adjacent_value(df, "All / Components / RAID")

    # 3. Determine output file name from cell A2 (row 1, column 0)
    output_name_raw = df.iat[1, 0]
    if pd.isna(output_name_raw) or not str(output_name_raw).strip():
        output_name = "output"
    else:
        output_name = str(output_name_raw).strip()
    output_file = f"{output_name}.ods"

    # Load the ODS template
    doc = ezodf.opendoc(template_ods)
    # Assume the first sheet is the target – adjust if needed
    sheet = doc.sheets[0]

    # 4. Populate the template ranges
    # B1:C2 → rows 0‑1, cols 1‑2
    set_range(sheet, 0, 1, 1, 2, suffix)

    # C10:D10 → row 9, cols 2‑3
    set_range(sheet, 9, 2, 9, 3, barebone_val)

    # C20:D23 → rows 19‑22, cols 2‑3
    set_range(sheet, 19, 2, 22, 3, gpu_val)

    # C12:D19 → rows 11‑18, cols 2‑3
    set_range(sheet, 11, 2, 18, 3, ram_val)

    # C11:D11 → row 10, cols 2‑3
    set_range(sheet, 10, 2, 10, 3, cpu_val)

    # C24:D25 → rows 23‑24, cols 2‑3
    set_range(sheet, 23, 2, 24, 3, ssd_val)

    # C27:D27 → row 26, cols 2‑3
    set_range(sheet, 26, 2, 26, 3, raid_val)

    # 5. Save the populated template as a new file
    doc.saveas(output_file)
    print(f"Populated template saved as: {output_file}")

