"""
shortcut_xl API Reference
All symbols are auto-imported in excel_exec — no import statements needed.
"""

from typing import Any


class Workbook:
    """
    Workbook wrapper.
    Usage:
        wb = Workbook(app.Workbooks("MyFile.xlsx"))

    Raw COM access:
        wb._wb  — the underlying COM Workbook object for direct platform API calls.
        Example: wb._wb.Worksheets("Sheet1").Visible = False
    """

    # @tags action,ask
    def getSheetNames(self) -> list[str]:
        """Get the names of all sheets in the workbook."""

    # @tags action,ask
    def getWorkbookSummary(self) -> str:
        """Get sheet names and used ranges for this workbook.
        Returns lines like:
            Sheets and their used ranges:
              Sheet1: A1:D10 (active)
              Inputs: (empty)
        """

    # @tags action,ask
    def getSheet(self, name: str) -> 'Worksheet':
        """Get a sheet by name, returned as a wrapped Worksheet."""

    # @tags action
    def addSheet(self, name: str, index: int | None = None) -> 'Worksheet':
        """Add a new sheet at 0-based index. index=0 inserts before the first sheet.
        Omit index for Excel's default position.
        Example: wb.addSheet("NewSheet", 0)  # insert as first sheet
        """

    # @tags action
    def moveSheet(self, name: str, index: int) -> None:
        """Move a sheet to 0-based index. index=0 moves to first, index=Count-1 moves to last.
        Example: wb.moveSheet("Data", 0)  # move to first position
        """

    # @tags action
    def calculate(self) -> None:
        """Recalculate the entire workbook.
        Dependent formulas don't update until after code block completes — read in a follow-up block.
        """

    # @tags action,ask
    def errorCheck(self, ranges: list[str] | None = None) -> str:
        """Scan for errors (#REF!, #DIV/0!, #NAME?, #VALUE!, #N/A, #NULL!, #NUM!). Ranges MUST include a sheet name ("Sheet1" or "Sheet1!A1:D50") — bare ranges like "A1:D50" will fail.
        Args:
            ranges: Optional sheet names ("Sheet1") or ranges ("Sheet1!A1:D50"). Omit to check all.
        Returns:
            "No issues found." or "Errors (N):\n  Sheet1!A1: #REF!\n  ..."
        """

    # @tags action
    def copyPasteRange(
        self,
        from_range: str,
        to_address: str,
        paste_type: str = 'all',  # "all" | "values" | "formatting" | "formulas"
    ) -> None:
        """Copy range within same workbook via range.Copy + PasteSpecial.
        Addresses must include sheet name. Do NOT use Sheet.Copy (broken via pywin32).
        Example: wb.copyPasteRange("Sheet1!A1:D10", "Sheet2!A1", paste_type="values")
        """

    # @tags action,ask
    def getNamedRangeInfo(self, max_count: int = 50) -> str:
        """Get named ranges. Returns formatted string with name, reference, scope, and comment.
        Example output: "Named Ranges (2):\\n  Name: TaxRate | Ref: =Settings!$B$2 | Scope: workbook | Comment: ..."
        Returns "No named ranges found." if none exist.
        """


class Worksheet:
    """
    Worksheet wrapper.
    Usage:
        sheet = wb.getSheet("Sheet1")

    Raw COM access:
        sheet._ws  — the underlying COM Worksheet object for direct platform API calls.
        Use for borders, text orientation, cell protection, and other properties not covered by the wrapper.
        Example: sheet._ws.Range("A1").Borders(9).LineStyle = 1  # xlEdgeBottom, xlContinuous
    """

    # @tags action,ask
    def getSheetSummary(self) -> str:
        """Overview: name, used range, tables (with style/banded/header/total props),
        pivots (with range), conditional formats (type/range/priority/stopIfTrue),
        autofilter (with filtering status), charts (with type)."""

    # @tags action,ask
    def getUsedRange(self) -> str:
        """Get the used range address like "A1:F20", or "" if empty."""

    # @tags action,ask
    def getCell(self, address: str, include_style: bool = True) -> str:
        """Formatted cell string: "value(=FORMULA) (style_json)". Formula always included.
        Zero with custom format appends [0]: "$ - [0]".
        Example:
            print(sheet.getCell("A1"))
            print(sheet.getCell("A1", include_style=False))
        """

    # @tags action,ask
    def getCellRange(self, range_addr: str, include_style: bool = True) -> str:
        """Markdown-like range: "A1:Name | B1:Value\\nA2:Alice | B2:100". Uses display text. Max 3000 cells; read larger areas in smaller chunks.
        Appends "--- Style patterns ---" section when include_style=True.
        """

    # @tags action,ask
    def getRawCellData(self, address: str, formula: bool = False) -> Any:
        """Get raw data value from a cell, preserving data type. Set formula=True to get the formula string."""

    # @tags action,ask
    def getRawRangeData(self, range_addr: str, formula: bool = False) -> list[list[Any]]:
        """Get raw data for a range as 2D list. Always returns 2D list even for single row/column."""

    # @tags action,ask
    def regexSearch(self, patterns: list[str], match_case: bool = False) -> list[dict]:
        """Regex search across used range using display text. Returns [{"address": "A1", "value": "display text"}, ...].
        Example: sheet.regexSearch(["revenue", "cost"])
        """

    # @tags action
    def setCell(
        self,
        address: str,
        value: Any,
        number_format: str | None = None,
        note: str | None = None,
    ) -> None:
        """Write value or formula. "=" prefix = formula. None or "" clears the cell.
        Examples:
            sheet.setCell("A1", 8000)
            sheet.setCell("C1", 0.15, number_format="0%", note="Tax rate")
            sheet.setCell("B1", "=SUM(A1:A10)")
        """

    # @tags action
    def setCellRange(
        self,
        range_addr: str,
        values: list[list[Any]],
        number_format: str | None = None,
    ) -> None:
        """Bulk write — ALWAYS use for >100 cells. "=" prefix = formula.
        Examples:
            sheet.setCellRange("A1:B2", [[1, 2], [3, 4]])
            sheet.setCellRange("A1:B2", [["=SUM(C1)", "=SUM(D1)"], [5, 6]], number_format="$#,##0")
        """

    # @tags action
    def autoFill(
        self,
        source_range: str,
        target_range: str,
        fill_mode: str = 'auto',  # "auto" | "constant"
    ) -> None:
        """Drag-fill. Source must be contained within target. Default "auto" increments hardcoded numeric literals; use fill_mode="constant" to copy literal values, then read back destination cells and check for empty/NaN values.
        Use fill_mode="constant" when copying a single value without incrementing.
        WARNING: Single numeric value with "auto" creates incrementing sequence (0,1,2,3...).
        Example: sheet.autoFill("A1:A2", "A1:A10")
        """

    # @tags action
    def addPicture(self, name: str, base64_data: str, anchor_cell: str) -> None:
        """Add a picture from base64-encoded PNG or JPEG data. No data URI prefix.
        Example: sheet.addPicture("chart1", base64_string, "F2")
        """

    # @tags action
    def setIBTextColors(
        self,
        range_addr: str,
        ignored_constants: list[int] | None = None,
        colors: dict | None = None,  # {"inputColor": BGR_int, "formulaColor": BGR_int, "crossSheetColor": BGR_int}
    ) -> None:
        """IB text colors for numeric inputs only. Text strings and booleans are left untouched.
        - Blue/16711680: hard-coded numeric constants (int/float)
        - Black/0: formulas (same-sheet references)
        - Green/32768: formulas with cross-sheet references (contains '!')
        Colors are BGR integers (native COM format).
        Example: sheet.setIBTextColors("A1:D10", ignored_constants=[0])
        """


# Color helper — COM uses BGR integers, not hex


# @tags action,ask
def hex_to_bgr(hex_str: str) -> int:
    """Convert "#RRGGBB" hex string to BGR integer for Excel COM.
    Example: ws.Range("A1").Font.Color = hex_to_bgr("#FF0000")  # red
    """


# Address helpers — 0-based indices


# @tags action,ask
def index_to_address(row: int, col: int) -> str:
    """Convert 0-based (row, col) to Excel address: index_to_address(0, 0) → "A1"."""


# @tags action,ask
def address_to_index(address: str) -> tuple[int, int]:
    """Parse "A1" into (row, col) 0-based tuple: address_to_index("B3") → (2, 1)."""


# @tags action,ask
def col_letter(index: int) -> str:
    """Convert 0-based column index to letter: col_letter(26) → "AA"."""


# @tags action,ask
def col_index(letter: str) -> int:
    """Convert column letter to 0-based index: col_index("AA") → 26."""
