//
//  Worksheet.swift
//  oosxl
//

import Foundation
import cxx_oosxl

public enum ErrorValue: Int, CustomStringConvertible, Sendable {
    case null = 0
    case divisionByZero = 1
    case value = 2
    case reference = 3
    case name = 4
    case number = 5
    case na = 6

    public var description: String {
        switch self {
        case .null: return "#NULL!"
        case .divisionByZero: return "#DIV/0!"
        case .value: return "#VALUE!"
        case .reference: return "#REF!"
        case .name: return "#NAME?"
        case .number: return "#NUM!"
        case .na: return "#N/A"
        }
    }

    static func from(raw: Int) -> ErrorValue {
        return ErrorValue(rawValue: raw) ?? .null
    }

    func toRaw() -> Int {
        return self.rawValue
    }
}

public struct SheetProtection: OptionSet, Sendable {
    public let rawValue: Int32

    public init(rawValue: Int32) {
        self.rawValue = rawValue
    }

    public static var selectLockedCells: SheetProtection { SheetProtection(rawValue: 1 << 0) }
    public static var selectUnlockedCells: SheetProtection { SheetProtection(rawValue: 1 << 1) }
    public static var objects: SheetProtection { SheetProtection(rawValue: 1 << 2) }
    public static var scenarios: SheetProtection { SheetProtection(rawValue: 1 << 3) }
    public static var formatCells: SheetProtection { SheetProtection(rawValue: 1 << 4) }
    public static var formatColumns: SheetProtection { SheetProtection(rawValue: 1 << 5) }
    public static var formatRows: SheetProtection { SheetProtection(rawValue: 1 << 6) }
    public static var insertColumns: SheetProtection { SheetProtection(rawValue: 1 << 7) }
    public static var insertRows: SheetProtection { SheetProtection(rawValue: 1 << 8) }
    public static var insertHyperlinks: SheetProtection { SheetProtection(rawValue: 1 << 9) }
    public static var deleteColumns: SheetProtection { SheetProtection(rawValue: 1 << 10) }
    public static var deleteRows: SheetProtection { SheetProtection(rawValue: 1 << 11) }
    public static var sort: SheetProtection { SheetProtection(rawValue: 1 << 12) }
    public static var autoFilter: SheetProtection { SheetProtection(rawValue: 1 << 13) }
    public static var pivotTables: SheetProtection { SheetProtection(rawValue: 1 << 14) }

    public static var `default`: SheetProtection {
        [
            .selectLockedCells, .selectUnlockedCells, .objects, .scenarios, .formatCells,
            .formatColumns, .formatRows,
        ]
    }

    public static var all: SheetProtection {
        [
            .selectLockedCells, .selectUnlockedCells, .objects, .scenarios, .formatCells,
            .formatColumns, .formatRows,
            .insertColumns, .insertRows, .insertHyperlinks, .deleteColumns, .deleteRows, .sort,
            .autoFilter, .pivotTables,
        ]
    }
}

public enum WorksheetError: Error, CustomStringConvertible, Sendable {
    case invalidCell(row: Int, col: Int)
    case invalidRow(row: Int)
    case invalidColumn(col: Int)
    case unsupportedType(type: Any.Type)
    case operationFailed(message: String)
    case dateConversionError
    case indexOutOfBounds(index: Int, count: Int)
    case concurrentAccessError

    public var description: String {
        switch self {
        case .invalidCell(let row, let col):
            return "Invalid cell coordinates: row=\(row), col=\(col)"
        case .invalidRow(let row): return "Invalid row: row=\(row)"
        case .invalidColumn(let col): return "Invalid column: column=\(col)"
        case .unsupportedType(let type): return "Unsupported type: \(type)"
        case .operationFailed(let message): return "Operation failed: \(message)"
        case .dateConversionError: return "Date conversion error"
        case .indexOutOfBounds(let index, let count):
            return "Index \(index) out of bounds (0..<\(count))"
        case .concurrentAccessError:
            return "Concurrent access detected. Use async methods for thread-safe operations."
        }
    }
}

public enum CellType: Int32, Sendable {
    case none = -1
    case empty
    case number
    case string
    case boolean
    case date
    case error
}

public enum DateMode: Sendable {
    case date1900
    case date1904
}

public enum CellValue: CustomStringConvertible, Equatable, Sendable {
    case empty
    case number(Double)
    case string(String)
    case boolean(Bool)
    case date(Date)
    case error(ErrorValue)
    case richtext(Richtext)
    case formula(String)

    public var description: String {
        switch self {
        case .empty: return "Empty"
        case .number(let num): return "Number(\(num))"
        case .string(let str): return "String(\(str))"
        case .boolean(let bool): return "Boolean(\(bool))"
        case .date(let date):
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            return "Date(\(formatter.string(from: date)))"
        case .error(let err): return "Error(\(err))"
        case .richtext(let rt): return "Richtext(\(rt.plainText))"
        case .formula(let expr): return "Formula(\(expr))"
        }
    }

    public static func == (lhs: CellValue, rhs: CellValue) -> Bool {
        switch (lhs, rhs) {
        case (.empty, .empty): return true
        case (.number(let l), .number(let r)): return l == r
        case (.string(let l), .string(let r)): return l == r
        case (.boolean(let l), .boolean(let r)): return l == r
        case (.date(let l), .date(let r)): return l == r
        case (.error(let l), .error(let r)): return l == r
        case (.formula(let l), .formula(let r)): return l == r
        case (.richtext(let l), .richtext(let r)): return l.plainText == r.plainText
        default: return false
        }
    }
}

// MARK: - Int extension
extension Int {
    // Convert column indexes to Excel column names（1-based）
    public func toExcelColumnName() -> String {
        var name = ""
        var value = self

        while value > 0 {
            let remainder = (value - 1) % 26
            name = String(UnicodeScalar(65 + remainder)!) + name
            value = (value - 1) / 26
        }

        return name
    }
}

// MARK: - String extension
extension String {
    // Convert Excel column names to column indexes（1-based）
    public func excelColumnToIndex() -> Int {
        var index = 0
        for char in self.uppercased() {
            index = index * 26 + Int(char.unicodeScalars.first!.value) - 64
        }
        return index
    }

    // Parse Excel range strings and return tuples of (starting row, starting column, ending row, ending column)（1-based）
    public func parseExcelRange() -> (startRow: Int, startColumn: Int, endRow: Int, endColumn: Int)?
    {
        // Supports two modes: single cell and range
        let singleCellPattern = "^([A-Za-z]+)(\\d+)$"
        let rangePattern = "^([A-Za-z]+)?(\\d+)?:([A-Za-z]+)?(\\d+)?$"

        // Try matching a single cell first
        if let singleRegex = try? NSRegularExpression(pattern: singleCellPattern),
            let singleMatch = singleRegex.firstMatch(
                in: self, options: [], range: NSRange(location: 0, length: self.utf16.count))
        {

            let colStr = (self as NSString).substring(with: singleMatch.range(at: 1))
            let rowStr = (self as NSString).substring(with: singleMatch.range(at: 2))

            guard let startRow = Int(rowStr), startRow > 0 else { return nil }
            let startColumn = colStr.excelColumnToIndex()

            // Single cell: Same start and end
            return (startRow, startColumn, startRow, startColumn)
        }

        // Try matching the range again
        if let rangeRegex = try? NSRegularExpression(pattern: rangePattern),
            let rangeMatch = rangeRegex.firstMatch(
                in: self, options: [], range: NSRange(location: 0, length: self.utf16.count))
        {

            var startRow: Int = 1
            var startColumn: Int = 1
            var endRow: Int = Int.max
            var endColumn: Int = Int.max

            if rangeMatch.range(at: 1).length > 0 {
                let colStr = (self as NSString).substring(with: rangeMatch.range(at: 1))
                startColumn = colStr.excelColumnToIndex()
            }

            if rangeMatch.range(at: 2).length > 0 {
                let rowStr = (self as NSString).substring(with: rangeMatch.range(at: 2))
                startRow = Int(rowStr) ?? 1
            }

            if rangeMatch.range(at: 3).length > 0 {
                let colStr = (self as NSString).substring(with: rangeMatch.range(at: 3))
                endColumn = colStr.excelColumnToIndex()
            }

            if rangeMatch.range(at: 4).length > 0 {
                let rowStr = (self as NSString).substring(with: rangeMatch.range(at: 4))
                endRow = Int(rowStr) ?? Int.max
            }

            if startColumn == 1 && endColumn == Int.max
                && (rangeMatch.range(at: 1).length == 0 && rangeMatch.range(at: 3).length == 0)
            {
                startColumn = 1
                endColumn = Int.max
            } else if startRow == 1 && endRow == Int.max
                && (rangeMatch.range(at: 2).length == 0 && rangeMatch.range(at: 4).length == 0)
            {
                startRow = 1
                endRow = Int.max
            }

            return (startRow, startColumn, endRow, endColumn)
        }

        return nil
    }
}

// MARK: - Cell Range
public struct CellRange: Sendable {
    public let worksheet: Worksheet
    public let startRow: Int
    public let startCol: Int
    public let endRow: Int
    public let endCol: Int

    public init(worksheet: Worksheet, row: Int, col: Int) {
        self.worksheet = worksheet
        self.startRow = row
        self.startCol = col
        self.endRow = row
        self.endCol = col
    }

    public init(worksheet: Worksheet, startRow: Int, startCol: Int, endRow: Int, endCol: Int) {
        self.worksheet = worksheet
        self.startRow = startRow
        self.startCol = startCol
        self.endRow = endRow
        self.endCol = endCol
    }

    public init?(worksheet: Worksheet, a1Notation: String) {
        guard let (startRow, startCol, endRow, endCol) = a1Notation.parseExcelRange() else {
            return nil
        }
        self.init(
            worksheet: worksheet, startRow: startRow, startCol: startCol, endRow: endRow,
            endCol: endCol)
    }
}

extension CellRange: Sequence {
    public func makeIterator() -> CellIterator {
        return CellIterator(range: self)
    }

    public struct CellIterator: IteratorProtocol {
        private let range: CellRange
        private var currentRow: Int
        private var currentCol: Int

        init(range: CellRange) {
            self.range = range
            self.currentRow = range.startRow
            self.currentCol = range.startCol
        }

        public mutating func next() -> Cell? {
            guard currentRow <= range.endRow else { return nil }

            let cell = Cell(worksheet: range.worksheet, row: currentRow, col: currentCol)

            currentCol += 1
            if currentCol > range.endCol {
                currentCol = range.startCol
                currentRow += 1
            }

            return cell
        }
    }
}

extension CellRange {
    public subscript(row: Int, col: Int) -> Cell {
        let absRow = startRow + row - 1
        let absCol = startCol + col - 1
        precondition(absRow <= endRow && absCol <= endCol, "Index out of range")
        return Cell(worksheet: worksheet, row: absRow, col: absCol)
    }

    public func isCell() -> Bool {
        return startRow == endRow && startCol == endCol
    }

    public func isEntireRow() -> Bool {
        return startCol == worksheet.minCol && endCol == worksheet.maxCol
    }

    public func isEntireCol() -> Bool {
        return startRow == worksheet.minRow && endRow == worksheet.maxRow
    }

    public var style: Style? {
        get {
            if startRow == endRow && startCol == endCol {
                return worksheet.getCellStyle(startRow, startCol)
            }
            return nil
        }
        set {
            if let style = newValue {
                worksheet.setRangeStyle(range: (startRow, startCol, endRow, endCol), style: style)
            }
        }
    }

    public func isDefaultRowHeight() -> Bool {
        return worksheet.isDefaultRowHeight(start: startRow, end: endRow)
    }

    public var customHeight: Double? {
        get {
            return worksheet.getCustomRowHeight(start: startRow, end: endRow)
        }
        set {
            if let newValue = newValue {
                worksheet.setCustomRowHeight(start: startRow, end: endRow, height: newValue)
            }
        }
    }

    public func isDefaultColWidth() -> Bool {
        return worksheet.isDefaultColWidth(start: startCol, end: endCol)
    }

    public var customWidth: Double? {
        get {
            return worksheet.getCustomColWidth(start: startCol, end: endCol)
        }
        set {
            if let newValue = newValue {
                worksheet.setCustomColWidth(start: startCol, end: endCol, width: newValue)
            }
        }
    }

    public func areRowsHidden() -> [Bool] {
        var hidden: [Bool] = []
        for row in startRow...endRow {
            hidden.append(worksheet.isRowHidden(row: row))
        }
        return hidden
    }

    public func isRowHidden() -> Bool? {
        let hidden = areRowsHidden()
        guard let firstHidden = hidden.first else { return nil }

        for h in hidden {
            if h != firstHidden {
                return nil
            }
        }
        return firstHidden
    }

    public func setRowHidden(_ hidden: Bool) {
        for row in startRow...endRow {
            worksheet.setRowHidden(row: row, hidden: hidden)
        }
    }

    public func areColumnsHidden() -> [Bool] {
        var hidden: [Bool] = []
        for column in startCol...endCol {
            hidden.append(worksheet.isColumnHidden(column: column))
        }
        return hidden
    }

    public func isColumnHidden() -> Bool? {
        let hidden = areColumnsHidden()
        guard let firstHidden = hidden.first else { return nil }

        for h in hidden {
            if h != firstHidden {
                return nil
            }
        }
        return firstHidden
    }

    public func setColumnHidden(_ hidden: Bool) {
        for column in startCol...endCol {
            worksheet.setColumnHidden(column: column, hidden: hidden)
        }
    }

    public func protect() -> Bool {
        return worksheet.protectRange(range: (startRow, startCol, endRow, endCol))
    }

    public func allowEditing() -> Bool {
        return worksheet.setEditableRange((startRow, startCol, endRow, endCol))
    }

    public func clearContents() {
        worksheet.clearRange((startRow, startCol, endRow, endCol))
    }
}

// MARK: - Cell
public struct Cell: Sendable {
    public let worksheet: Worksheet
    public let row: Int
    public let col: Int

    public init(worksheet: Worksheet, row: Int, col: Int) {
        self.worksheet = worksheet
        self.row = row
        self.col = col
    }

    public var range: CellRange {
        return CellRange(worksheet: worksheet, row: row, col: col)
    }

    public var address: String {
        return columnName + "\(row)"
    }

    public var columnName: String {
        return col.toExcelColumnName()
    }

    public var type: CellType {
        let row_ = Int32(row)
        let col_ = Int32(col)
        return CellType(rawValue: ws_cell_type(worksheet.handle, row_, col_)) ?? .none
    }

    public var value: CellValue? {
        get {
            return worksheet.get(cell: (self.row, self.col))
        }
        set {
            if let newValue = newValue {
                worksheet.setValue(newValue, to: (self.row, self.col))
            } else {
                worksheet.clearCell(at: (self.row, self.col))
            }
        }
    }

    public var style: Style? {
        get {
            return worksheet.getCellStyle(self.row, self.col)
        }
        set {
            if let style = newValue {
                worksheet.setCellStyle(self.row, self.col, style)
            }
        }
    }
}

extension Cell {
    public var hasFormula: Bool {
        worksheet.lock.lock()
        defer { worksheet.lock.unlock() }
        return ws_contains_formula(self.worksheet.handle, Int32(self.row), Int32(self.col)) != 0
    }

    public var formula: String? {
        get {
            worksheet.lock.lock()
            defer { worksheet.lock.unlock() }
            if let expr = ws_get_formula_expr(worksheet.handle, Int32(row), Int32(col)) {
                return String(cString: expr)
            }
            return nil
        }
        set {
            worksheet.lock.lock()
            defer { worksheet.lock.unlock() }
            if let expression = newValue {
                let _ = expression.withCString { ptr in
                    ws_set_formula(worksheet.handle, Int32(row), Int32(col), ptr)
                }
            } else {
                ws_clear_contents(worksheet.handle, Int32(row), Int32(col), Int32(row), Int32(col))
            }
        }
    }

    public var hasRichtext: Bool {
        return ws_contains_richtext(worksheet.handle, Int32(row), Int32(col)) != 0
    }

    public var richtext: Richtext? {
        get {
            if let handle = ws_get_richtext(worksheet.handle, Int32(row), Int32(col)) {
                return Richtext(withHandle: handle)
            }
            return nil
        }
        set {
            if let newValue = newValue {
                worksheet.setRichText(newValue, to: (row, col))
            }
        }
    }

    public var isHidden: Bool {
        let cRow = Int32(row)
        let cCol = Int32(col)
        let rowHidden = ws_row_hidden(worksheet.handle, cRow) != 0
        let colHidden = ws_col_hidden(worksheet.handle, cCol) != 0
        return rowHidden || colHidden
    }

    public func clear() {
        worksheet.clearCell(at: (row, col))
    }
}

// MARK: - Make Cell Comparable and Hashable
extension Cell: Equatable {
    public static func == (lhs: Cell, rhs: Cell) -> Bool {
        return lhs.worksheet === rhs.worksheet && lhs.row == rhs.row && lhs.col == rhs.col
    }
}

extension Cell: Hashable {
    public func hash(into hasher: inout Hasher) {
        hasher.combine(ObjectIdentifier(worksheet))
        hasher.combine(row)
        hasher.combine(col)
    }
}

// MARK: - Worksheet class
public class Worksheet: @unchecked Sendable {
    public unowned let workbook: Workbook
    let handle: WorksheetHandle
    let lock = NSRecursiveLock()

    init(withHandle handle: WorksheetHandle, workbook: Workbook) {
        self.handle = handle
        self.workbook = workbook
    }

    deinit {
    }
}

extension Worksheet {
    public var minRow: Int {
        return Int(ws_min_row(self.handle))
    }

    public var maxRow: Int {
        return Int(ws_max_row(self.handle))
    }

    public var minCol: Int {
        return Int(ws_min_col(self.handle))
    }

    public var maxCol: Int {
        return Int(ws_max_col(self.handle))
    }

    public func range(_ a1Notation: String) -> CellRange? {
        guard let (startRow, startCol, endRow, endCol) = a1Notation.parseExcelRange() else {
            return nil
        }
        return CellRange(
            worksheet: self, startRow: startRow, startCol: startCol, endRow: endRow, endCol: endCol)
    }

    public func range(_ startRow: Int, _ startCol: Int, _ endRow: Int, _ endCol: Int) -> CellRange {
        return CellRange(
            worksheet: self, startRow: startRow, startCol: startCol, endRow: endRow, endCol: endCol)
    }

    public subscript(row: Int, col: Int) -> Cell {
        return Cell(worksheet: self, row: row, col: col)
    }

    public func isValidCell(_ cell: Cell) -> Bool {
        return cell.row >= minRow && cell.row <= maxRow && cell.col >= minCol && cell.col <= maxCol
    }

    public func isValidRange(_ range: CellRange) -> Bool {
        return range.startRow >= minRow && range.startRow <= maxRow
            && range.startCol >= minCol && range.startCol <= maxCol
            && range.endRow >= minRow && range.endRow <= maxRow
            && range.endCol >= minCol && range.endCol <= maxCol
    }
}

extension Worksheet {
    public func type(ofCell cell: (Int, Int)) -> CellType {
        let row = Int32(cell.0)
        let col = Int32(cell.1)
        return CellType(rawValue: ws_cell_type(self.handle, row, col)) ?? .none
    }

    @discardableResult
    public func set<T>(_ value: T, _ cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }

        if let numberValue = value as? any Numeric {
            return setNumericValue(numberValue, at: cell)
        }

        if let stringValue = value as? String {
            return setString(stringValue, to: cell)
        }

        if let boolValue = value as? Bool {
            return setBoolean(boolValue, to: cell)
        }

        if let dateValue = value as? Date {
            return setDate(dateValue, to: cell)
        }

        if let richtextValue = value as? Richtext {
            return setRichText(richtextValue, to: cell)
        }

        if let errorValue = value as? ErrorValue {
            return setError(errorValue, to: cell)
        }

        return false
    }

    private func setNumericValue(_ value: any Numeric, at cell: (Int, Int)) -> Bool {
        let doubleValue: Double

        switch value {
        case let int as Int: doubleValue = Double(int)
        case let int8 as Int8: doubleValue = Double(int8)
        case let int16 as Int16: doubleValue = Double(int16)
        case let int32 as Int32: doubleValue = Double(int32)
        case let int64 as Int64: doubleValue = Double(int64)
        case let uint as UInt: doubleValue = Double(uint)
        case let uint8 as UInt8: doubleValue = Double(uint8)
        case let uint16 as UInt16: doubleValue = Double(uint16)
        case let uint32 as UInt32: doubleValue = Double(uint32)
        case let uint64 as UInt64: doubleValue = Double(uint64)
        case let float as Float: doubleValue = Double(float)
        case let double as Double: doubleValue = double
        default: return false
        }

        return ws_set_num(self.handle, Int32(cell.0), Int32(cell.1), doubleValue) != 0
    }

    @discardableResult
    public func setNumber(_ value: Double, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_set_num(self.handle, Int32(cell.0), Int32(cell.1), value) != 0
    }

    @discardableResult
    public func setString(_ text: String, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        let result = text.withCString { ptr in
            return ws_set_text(self.handle, Int32(cell.0), Int32(cell.1), ptr)
        }
        return result != 0
    }

    @discardableResult
    public func setBoolean(_ value: Bool, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_set_boolean(self.handle, Int32(cell.0), Int32(cell.1), value ? 1 : 0) != 0
    }

    @discardableResult
    public func setDate(_ date: Date, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        let excelDate = oosxl.encodeDatetime(date: date)
        return ws_set_datetime(self.handle, Int32(cell.0), Int32(cell.1), excelDate) != 0
    }

    @discardableResult
    public func setRichText(_ richtext: Richtext, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_set_richtext(self.handle, Int32(cell.0), Int32(cell.1), richtext.handle) != 0
    }

    @discardableResult
    public func setError(_ error: ErrorValue, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_set_error(self.handle, Int32(cell.0), Int32(cell.1), Int32(error.toRaw())) != 0
    }

    @discardableResult
    public func setFormula(_ formula: String, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }

        let result = formula.withCString { ptr in
            return ws_set_formula(self.handle, Int32(cell.0), Int32(cell.1), ptr)
        }
        return result != 0
    }

    @discardableResult
    public func setValue(_ value: CellValue, to cell: (Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }

        switch value {
        case .empty:
            clearCell(at: cell)
            return true
        case .number(let num):
            return setNumber(num, to: cell)
        case .string(let text):
            return setString(text, to: cell)
        case .boolean(let bool):
            return setBoolean(bool, to: cell)
        case .date(let date):
            return setDate(date, to: cell)
        case .error(let error):
            return setError(error, to: cell)
        case .richtext(let richtext):
            return setRichText(richtext, to: cell)
        case .formula(let formula):
            return setFormula(formula, to: cell)
        }
    }

    public func getDate(cell: (Int, Int)) -> Double {
        return ws_get_datetime(self.handle, Int32(cell.0), Int32(cell.1))
    }

    public func get(cell: (Int, Int)) -> CellValue? {
        lock.lock()
        defer { lock.unlock() }

        let row = Int32(cell.0)
        let col = Int32(cell.1)
        let rawType = ws_cell_type(self.handle, row, col)

        guard let type = CellType(rawValue: rawType), type != .none else {
            return nil
        }

        switch type {
        case .empty:
            return .empty
        case .number:
            let num = ws_get_num(self.handle, row, col)
            return .number(num)
        case .string:
            guard let text = ws_get_text(handle, row, col) else { return .string("") }
            return .string(String(cString: text))
        case .boolean:
            let b = ws_get_boolean(self.handle, row, col)
            return .boolean(b != 0)
        case .date:
            let value = ws_get_datetime(self.handle, row, col)
            let datetime =
                workbook.dateMode == .date1900
                ? oosxl.decodeDatetime(doubleValue: value).date!
                : oosxl.decodeDatetime(doubleValue: value, isDate1904: true).date!
            return .date(datetime)
        case .error:
            let err = ws_get_error(self.handle, row, col)
            return .error(ErrorValue.from(raw: Int(err)))
        default:
            return nil
        }
    }

    public func getNumber(at cell: (Int, Int)) -> Double? {
        guard let value = get(cell: cell),
            case .number(let number) = value
        else { return nil }
        return number
    }

    public func getString(at cell: (Int, Int)) -> String? {
        guard let value = get(cell: cell),
            case .string(let text) = value
        else { return nil }
        return text
    }

    public func getBoolean(at cell: (Int, Int)) -> Bool? {
        guard let value = get(cell: cell),
            case .boolean(let flag) = value
        else { return nil }
        return flag
    }

    public func clearCell(at cell: (Int, Int)) {
        lock.lock()
        defer { lock.unlock() }
        ws_clear_contents(self.handle, Int32(cell.0), Int32(cell.1), Int32(cell.0), Int32(cell.1))
    }

    public func clearRange(_ range: (Int, Int, Int, Int)) {
        lock.lock()
        defer { lock.unlock() }
        ws_clear_contents(
            self.handle, Int32(range.0), Int32(range.1), Int32(range.2), Int32(range.3))
    }
}

// MARK: - Style operation
extension Worksheet {
    public func getCellStyle(_ row: Int, _ col: Int) -> Style? {
        lock.lock()
        defer { lock.unlock() }

        if let handle = ws_cell_style(handle, Int32(row), Int32(col)) {
            return Style(withHandle: handle, workbook: workbook)
        }
        return nil
    }

    public func setCellStyle(_ row: Int, _ col: Int, _ style: Style) {
        lock.lock()
        defer { lock.unlock() }
        ws_set_cell_style(self.handle, Int32(row), Int32(col), style.handle)
    }

    public func setCellStyle(_ row: Int, _ col: Int, _ styleName: String) {
        lock.lock()
        defer { lock.unlock() }

        guard let style = workbook.getNamedStyle(styleName) else { return }
        ws_set_cell_style(self.handle, Int32(row), Int32(col), style.handle)
    }

    public func setRangeStyle(range: (Int, Int, Int, Int), style: Style) {
        lock.lock()
        defer { lock.unlock() }

        let row1 = Int32(range.0)
        let col1 = Int32(range.1)
        let row2 = Int32(range.2)
        let col2 = Int32(range.3)
        ws_set_range_style(self.handle, row1, col1, row2, col2, style.handle)
    }

    public func setRangeStyle(range: (Int, Int, Int, Int), styleName: String) {
        lock.lock()
        defer { lock.unlock() }

        guard let style = workbook.getNamedStyle(styleName) else { return }

        let row1 = Int32(range.0)
        let col1 = Int32(range.1)
        let row2 = Int32(range.2)
        let col2 = Int32(range.3)
        ws_set_range_style(self.handle, row1, col1, row2, col2, style.handle)
    }
}

// MARK: - Row and column operation
extension Worksheet {
    public func isDefaultRowHeight(start: Int, end: Int) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_is_default_row_height(handle, Int32(start), Int32(end)) != 0
    }

    public func getCustomRowHeight(start: Int, end: Int, unit: RowHeightUnit = .points) -> Double? {
        lock.lock()
        defer { lock.unlock() }

        var height: Double = 0.0
        let result =
            ws_get_custom_row_height(
                handle, Int32(start), Int32(end), &height, Int32(unit.rawValue)) != 0
        return result ? height : nil
    }

    public func setCustomRowHeight(
        start: Int, end: Int, height: Double, unit: RowHeightUnit = .points
    ) {
        lock.lock()
        defer { lock.unlock() }
        ws_set_custom_row_height(handle, Int32(start), Int32(end), height, Int32(unit.rawValue))
    }

    public func isDefaultColWidth(start: Int, end: Int) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_is_default_col_width(handle, Int32(start), Int32(end)) != 0
    }

    public func getCustomColWidth(start: Int, end: Int, unit: ColWidthUnit = .chars) -> Double? {
        lock.lock()
        defer { lock.unlock() }

        var width: Double = 0.0
        let result =
            ws_get_custom_col_width(handle, Int32(start), Int32(end), &width, Int32(unit.rawValue))
            != 0
        return result ? width : nil
    }

    public func setCustomColWidth(start: Int, end: Int, width: Double, unit: ColWidthUnit = .chars)
    {
        lock.lock()
        defer { lock.unlock() }
        ws_set_custom_col_width(handle, Int32(start), Int32(end), width, Int32(unit.rawValue))
    }

    public func isRowHidden(row: Int) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_row_hidden(handle, Int32(row)) != 0
    }

    public func setRowHidden(row: Int, hidden: Bool) {
        lock.lock()
        defer { lock.unlock() }
        ws_set_row_hidden(handle, Int32(row), Int32(row), hidden ? 1 : 0)
    }

    public func setRowHidden(startRow: Int, endRow: Int, hidden: Bool) {
        lock.lock()
        defer { lock.unlock() }
        ws_set_row_hidden(handle, Int32(startRow), Int32(endRow), hidden ? 1 : 0)
    }

    public func isColumnHidden(column: Int) -> Bool {
        lock.lock()
        defer { lock.unlock() }
        return ws_col_hidden(handle, Int32(column)) != 0
    }

    public func setColumnHidden(column: Int, hidden: Bool) {
        lock.lock()
        defer { lock.unlock() }
        ws_set_col_hidden(handle, Int32(column), Int32(column), hidden ? 1 : 0)
    }

    public func setColumnHidden(startColumn: Int, endColumn: Int, hidden: Bool) {
        lock.lock()
        defer { lock.unlock() }
        ws_set_col_hidden(handle, Int32(startColumn), Int32(endColumn), hidden ? 1 : 0)
    }

    public func protect(_ protections: SheetProtection = .default, password: String) -> Bool {
        lock.lock()
        defer { lock.unlock() }

        let result = password.withCString { ptr in
            return ws_protect(self.handle, protections.rawValue, ptr)
        }
        return result != 0
    }

    public func unprotect() {
        lock.lock()
        defer { lock.unlock() }
        ws_unprotect(self.handle)
    }

    public func protectRange(range: (Int, Int, Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }

        let row1 = Int32(range.0)
        let col1 = Int32(range.1)
        let row2 = Int32(range.2)
        let col2 = Int32(range.3)
        return ws_protect_range(self.handle, row1, col1, row2, col2) != 0
    }

    public func setEditableRange(_ range: (Int, Int, Int, Int)) -> Bool {
        lock.lock()
        defer { lock.unlock() }

        let row1 = Int32(range.0)
        let col1 = Int32(range.1)
        let row2 = Int32(range.2)
        let col2 = Int32(range.3)
        return ws_set_editable_range(self.handle, row1, col1, row2, col2) != 0
    }
}

// MARK: - Other worksheet operations (merging cells, inserting and deleting rows and columns, etc.)
extension Worksheet {
    public var mergedNumber: Int {
        return Int(ws_merged_num(self.handle))
    }

    public func mergedRange(index: Int) -> (Int, Int, Int, Int)? {
        var startRow: Int32 = 0
        var startCol: Int32 = 0
        var stopRow: Int32 = 0
        var stopCol: Int32 = 0

        let result = ws_merged_range(
            self.handle, Int32(index), &startRow, &startCol, &stopRow, &stopCol)

        guard result != 0 else {
            return nil
        }

        return (Int(startRow), Int(startCol), Int(stopRow), Int(stopCol))
    }

    public func merge(range: (Int, Int, Int, Int)) -> Bool {
        return ws_merge(self.handle, Int32(range.0), Int32(range.1), Int32(range.2), Int32(range.3))
            != 0
    }

    public func unmerge(cell: (Int, Int)) -> Bool {
        return ws_unmerge(self.handle, Int32(cell.0), Int32(cell.1)) != 0
    }

    public func insertRow(above row: Int, count: Int) -> Bool {
        return ws_insert_row(self.handle, Int32(row), Int32(count)) != 0
    }

    public func deleteRow(from start: Int, to end: Int) -> Bool {
        return ws_delete_row(self.handle, Int32(start), Int32(end)) != 0
    }

    public func deleteRow(_ row: Int) -> Bool {
        return ws_delete_row(self.handle, Int32(row), Int32(row)) != 0
    }

    public func insertCol(leftOf col: Int, count: Int) -> Bool {
        return ws_insert_col(self.handle, Int32(col), Int32(count)) != 0
    }

    public func deleteCol(from start: Int, to end: Int) -> Bool {
        return ws_delete_col(self.handle, Int32(start), Int32(end)) != 0
    }

    public func deleteCol(_ col: Int) -> Bool {
        return ws_delete_col(self.handle, Int32(col), Int32(col)) != 0
    }

    public var firstDataCell: Cell? {
        var row: Int32 = 0
        var col: Int32 = 0

        if ws_first_data_cell(self.handle, &row, &col) != 0 {
            return Cell(worksheet: self, row: Int(row), col: Int(col))
        }
        return nil
    }

    public var lastDataCell: Cell? {
        var row: Int32 = 0
        var col: Int32 = 0

        if ws_last_data_cell(self.handle, &row, &col) != 0 {
            return Cell(worksheet: self, row: Int(row), col: Int(col))
        }
        return nil
    }
}

public enum ImageAnchorEnum: Int32, Sendable {
    case notExist = 0
    case absolute
    case withOneCell
    case withTwoCells

    public init(rawValue: Int32) {
        switch rawValue {
        case 0: self = .notExist
        case 1: self = .absolute
        case 2: self = .withOneCell
        case 3: self = .withTwoCells
        default: self = .notExist
        }
    }
}

// MARK: - image and background
extension Worksheet {
    public func setBackgroundPicture(_ file: String) {
        ws_set_bg_picture(handle, file)
    }

    @discardableResult
    public func addCellImage(
        _ file: String,
        startRow: Int, startCol: Int,
        stopRow: Int, stopCol: Int
    ) -> Int {
        return Int(
            ws_add_cell_image(
                handle, file,
                Int32(startRow), Int32(startCol),
                Int32(stopRow), Int32(stopCol)))
    }

    @discardableResult
    public func addImageWithTwoCells(
        _ file: String,
        row1: Int, col1: Int,
        row2: Int, col2: Int,
        offsetX1: Int = 0, offsetY1: Int = 0,
        offsetX2: Int = 0, offsetY2: Int = 0
    ) -> Int {
        return Int(
            ws_add_image_with_two_cells(
                handle, file,
                Int32(row1), Int32(col1),
                Int32(row2), Int32(col2),
                Int32(offsetX1), Int32(offsetY1),
                Int32(offsetX2), Int32(offsetY2)))
    }

    @discardableResult
    public func addImageWithOneCell(
        _ file: String,
        row: Int, col: Int,
        width: Int, height: Int,
        scale: Double = 1.0,
        offsetX: Int = 0, offsetY: Int = 0
    ) -> Int {
        return Int(
            ws_add_image_with_one_cell(
                handle, file,
                Int32(row), Int32(col),
                Int32(width), Int32(height),
                scale, Int32(offsetX), Int32(offsetY)))
    }

    @discardableResult
    public func addAbsoluteImage(
        _ file: String,
        x: Int, y: Int,
        width: Int = 0, height: Int = 0,
        scale: Double = 1.0
    ) -> Int {
        return Int(
            ws_add_absolute_image(
                handle, file,
                Int32(x), Int32(y),
                Int32(width), Int32(height),
                scale))
    }

    public var imageCount: Int {
        return Int(ws_image_num(handle))
    }

    public func getImageId(at index: Int) -> Int? {
        let id = ws_get_image_id(handle, Int32(index))
        return id >= 0 ? Int(id) : nil
    }

    public func imageAnchorType(for imageId: Int) -> ImageAnchorEnum {
        return ImageAnchorEnum(rawValue: ws_image_anchor_type(handle, Int32(imageId)))
    }

    public func getAnchoredCell(for imageId: Int) -> (row: Int, col: Int)? {
        var row: Int32 = 0
        var col: Int32 = 0
        let result = ws_get_anchored_cell(handle, Int32(imageId), &row, &col)

        if result == 0 {
            return (Int(row), Int(col))
        }
        return nil
    }

    public func getAbsoluteCoordinates(for imageId: Int) -> (x: Int, y: Int)? {
        var x: Int32 = 0
        var y: Int32 = 0
        let result = ws_get_absolute_coord(handle, Int32(imageId), &x, &y)

        if result == 0 {
            return (Int(x), Int(y))
        }
        return nil
    }

    public func deleteImage(_ imageId: Int) {
        ws_delete_image(handle, Int32(imageId))
    }

    public func removeImage(atRow row: Int, col: Int) {
        ws_remove_image(handle, Int32(row), Int32(col))
    }

    public func removeAbsoluteImage(atX x: Int, y: Int) {
        ws_remove_absolute_image(handle, Int32(x), Int32(y))
    }
}

// MARK: - Grouping operation
extension Worksheet {
    public func groupRows(from startRow: Int, to endRow: Int, collapsed: Bool = true) {
        ws_group_rows(handle, Int32(startRow), Int32(endRow), collapsed ? 1 : 0)
    }

    public func ungroupRows(from startRow: Int, to endRow: Int) {
        ws_ungroup_rows(handle, Int32(startRow), Int32(endRow))
    }

    public func groupColumns(from startCol: Int, to endCol: Int, collapsed: Bool = true) {
        ws_group_cols(handle, Int32(startCol), Int32(endCol), collapsed ? 1 : 0)
    }

    public func ungroupColumns(from startCol: Int, to endCol: Int) {
        ws_ungroup_cols(handle, Int32(startCol), Int32(endCol))
    }

    public func rowOutlineLevel(_ row: Int) -> (level: Int, collapsed: Bool)? {
        var collapsed: Int32 = 0
        let level = ws_row_outline_level(handle, Int32(row), &collapsed)
        guard level >= 0 else { return nil }
        return (Int(level), collapsed != 0)
    }

    public func columnOutlineLevel(_ col: Int) -> (level: Int, collapsed: Bool)? {
        var collapsed: Int32 = 0
        let level = ws_col_outline_level(handle, Int32(col), &collapsed)
        guard level >= 0 else { return nil }
        return (Int(level), collapsed != 0)
    }

    public var maxRowOutlineLevel: Int {
        Int(ws_max_row_outline_level(handle))
    }

    public var maxColumnOutlineLevel: Int {
        Int(ws_max_col_outline_level(handle))
    }

    public func deleteRowOutline() {
        ws_delete_row_outline(handle)
    }

    public func deleteColumnOutline() {
        ws_delete_col_outline(handle)
    }

    public var groupSummaryBelow: Bool {
        get { ws_group_summary_below(handle) != 0 }
        set { ws_set_group_summary_below(handle, newValue ? 1 : 0) }
    }

    public var groupSummaryRight: Bool {
        get { ws_group_summary_right(handle) != 0 }
        set { ws_set_group_summary_right(handle, newValue ? 1 : 0) }
    }
}

// MARK: - Hyperlink operation
extension Worksheet {
    public var hyperlinkCount: Int {
        Int(ws_hyperlink_num(handle))
    }

    public func addHyperlink(_ hyperlink: String, in range: CellRange) -> Bool {
        hyperlink.withCString {
            ws_add_hyperlink(
                handle, $0, Int32(range.startRow), Int32(range.startCol),
                Int32(range.endRow), Int32(range.endCol)) != 0
        }
    }

    public func hyperlinkIndex(at cell: (Int, Int)) -> Int? {
        let index = ws_hyperlink_index(handle, Int32(cell.0), Int32(cell.1))
        return index >= 0 ? Int(index) : nil
    }

    public func hyperlink(at index: Int) -> (hyperlink: String, Int, Int, Int, Int)? {
        var row1: Int32 = 0
        var col1: Int32 = 0
        var row2: Int32 = 0
        var col2: Int32 = 0
        guard let hyperlink = ws_hyperlink(handle, Int32(index), &row1, &col1, &row2, &col2) else {
            return nil
        }
        return (String(cString: hyperlink), Int(row1), Int(col1), Int(row2), Int(col2))
    }

    public func deleteHyperlink(at index: Int) -> Bool {
        ws_delete_hyperlink(handle, Int32(index)) != 0
    }
}

// MARK: - Table operation
extension Worksheet {
    public var tabledefCount: Int {
        Int(ws_tabledef_count(handle))
    }

    public func tabledefName(at index: Int) -> String? {
        ws_get_tabledef_name(handle, Int32(index)).map(String.init(cString:))
    }

    public func setTabledefName(_ name: String, at index: Int) -> Bool {
        name.withCString {
            ws_set_tabledef_name(handle, Int32(index), $0) != 0
        }
    }

    public func tabledef(at index: Int) -> Tabledef? {
        guard let handle = ws_get_tabledef(handle, Int32(index)) else { return nil }
        return Tabledef(withHandle: handle, worksheet: self)
    }

    public func tabledef(named name: String) -> Tabledef? {
        let handle = name.withCString { cString in
            return ws_get_tabledef_by_name(self.handle, cString)
        }
        guard handle != nil else { return nil }
        return Tabledef(withHandle: handle!, worksheet: self)
    }

    public func addTabledef(name: String, range: (Int, Int, Int, Int)) -> Tabledef? {
        let row1 = Int32(range.0)
        let col1 = Int32(range.1)
        let row2 = Int32(range.2)
        let col2 = Int32(range.3)

        let handle = name.withCString { cString in
            return ws_add_tabledef(self.handle, cString, row1, col1, row2, col2)
        }
        guard handle != nil else { return nil }
        return Tabledef(withHandle: handle!, worksheet: self)
    }

    public func addTabledef(name: String, range: CellRange) -> Tabledef? {
        return addTabledef(
            name: name,
            range: (range.startRow, range.startCol, range.endRow, range.endCol))
    }

    public func deleteTabledef(named name: String) -> Bool {
        name.withCString {
            ws_delete_tabledef(handle, $0) != 0
        }
    }
}

// MARK: - Page break
public struct HPageBreak: Sendable {
    public let row: Int

    public init(row: Int) {
        self.row = row
    }
}

public struct VPageBreak: Sendable {
    public let column: Int

    public init(column: Int) {
        self.column = column
    }
}

public struct HPageBreaks: Sequence, Sendable {
    private let worksheet: Worksheet

    init(worksheet: Worksheet) {
        self.worksheet = worksheet
    }

    public var count: Int {
        Int(ws_row_break_count(worksheet.handle))
    }

    public func addBreak(at row: Int) -> Bool {
        ws_set_row_break(worksheet.handle, Int32(row), 1) != 0
    }

    public func makeIterator() -> PageBreakIterator<HPageBreak> {
        PageBreakIterator(worksheet: worksheet, isHorizontal: true)
    }
}

public struct VPageBreaks: Sequence, Sendable {
    private let worksheet: Worksheet

    init(worksheet: Worksheet) {
        self.worksheet = worksheet
    }

    public var count: Int {
        Int(ws_col_break_count(worksheet.handle))
    }

    public func addBreak(at column: Int) -> Bool {
        ws_set_col_break(worksheet.handle, Int32(column), 1) != 0
    }

    public func makeIterator() -> PageBreakIterator<VPageBreak> {
        PageBreakIterator(worksheet: worksheet, isHorizontal: false)
    }
}

public struct PageBreakIterator<BreakType>: IteratorProtocol where BreakType: Sendable {
    private let worksheet: Worksheet
    private let isHorizontal: Bool
    private var currentIndex: Int = 0
    private let count: Int

    init(worksheet: Worksheet, isHorizontal: Bool) {
        self.worksheet = worksheet
        self.isHorizontal = isHorizontal
        self.count =
            isHorizontal
            ? Int(ws_row_break_count(worksheet.handle)) : Int(ws_col_break_count(worksheet.handle))
    }

    public mutating func next() -> BreakType? {
        guard currentIndex < count else { return nil }

        if isHorizontal {
            let row = Int(ws_row_break(worksheet.handle, Int32(currentIndex)))
            currentIndex += 1
            return HPageBreak(row: row) as? BreakType
        } else {
            let col = Int(ws_col_break(worksheet.handle, Int32(currentIndex)))
            currentIndex += 1
            return VPageBreak(column: col) as? BreakType
        }
    }
}

extension Worksheet {
    public var horizontalPageBreaks: HPageBreaks {
        HPageBreaks(worksheet: self)
    }

    public var verticalPageBreaks: VPageBreaks {
        VPageBreaks(worksheet: self)
    }
}

// MARK: - 工作表属性
extension Worksheet {
    public var isProtect: Bool {
        return ws_is_protected(handle) != 0
    }

    public var zoom: Int {
        get { Int(ws_zoom(handle)) }
        set { ws_set_zoom(handle, Int32(newValue)) }
    }

    public var topLeftCell: (Int, Int) {
        get {
            var row: Int32 = 0
            var col: Int32 = 0
            ws_topleft_cell(handle, &row, &col)
            return (Int(row), Int(col))
        }
        set {
            ws_set_topleft_cell(handle, Int32(newValue.0), Int32(newValue.1))
        }
    }

    public var activeCell: (Int, Int) {
        get {
            var row: Int32 = 0
            var col: Int32 = 0
            ws_active_cell(handle, &row, &col)
            return (Int(row), Int(col))
        }
        set {
            ws_set_active_cell(handle, Int32(newValue.0), Int32(newValue.1))
        }
    }

    public var displayGridLines: Bool {
        get { ws_display_gridlines(handle) != 0 }
        set { ws_set_display_gridlines(handle, newValue ? 1 : 0) }
    }

    public var printGridLines: Bool {
        get { ws_print_gridlines(handle) != 0 }
        set { ws_set_print_gridlines(handle, newValue ? 1 : 0) }
    }
}

// MARK: - Asynchronous support
extension Worksheet {
    @discardableResult
    public func setAsync(_ value: CellValue, at cell: (Int, Int)) async -> Bool {
        return await withCheckedContinuation { continuation in
            DispatchQueue.global(qos: .userInitiated).async {
                let result = self.setValue(value, to: cell)
                continuation.resume(returning: result)
            }
        }
    }

    public func setValuesAsync(_ values: [[CellValue]], startingAt startCell: (Int, Int))
        async throws
    {
        try await withThrowingTaskGroup(of: Bool.self) { group in
            for (rowOffset, rowValues) in values.enumerated() {
                for (colOffset, value) in rowValues.enumerated() {
                    let cell = (startCell.0 + rowOffset, startCell.1 + colOffset)

                    group.addTask {
                        await self.setAsync(value, at: cell)
                    }
                }
            }

            for try await result in group {
                if !result {
                    throw WorksheetError.operationFailed(
                        message: "Failed to set cell value asynchronously")
                }
            }
        }
    }

    public func getAsync(cell: (Int, Int)) async -> CellValue? {
        return await withCheckedContinuation { continuation in
            DispatchQueue.global(qos: .userInitiated).async {
                let value = self.get(cell: cell)
                continuation.resume(returning: value)
            }
        }
    }
}
