[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "docuelevate"
dynamic = ["version"]
description = "Intelligent document processing system with AI-powered metadata extraction"
readme = "README.md"
requires-python = ">=3.11"
license = {text = "Apache-2.0"}
authors = [
    {name = "Christian Louis", email = "christianlouis@users.noreply.github.com"}
]
keywords = ["document", "processing", "ocr", "ai", "metadata", "extraction"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: Apache Software License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
]

[project.urls]
Homepage = "https://github.com/christianlouis/DocuElevate"
Documentation = "https://github.com/christianlouis/DocuElevate/tree/main/docs"
Repository = "https://github.com/christianlouis/DocuElevate"
Issues = "https://github.com/christianlouis/DocuElevate/issues"
Changelog = "https://github.com/christianlouis/DocuElevate/blob/main/CHANGELOG.md"

[tool.setuptools.dynamic]
version = {file = "VERSION"}

[project.scripts]
docuelevate = "app.cli:main"

[tool.semantic_release]
version_toml = []
version_source = "tag"
commit_version_number = true
tag_format = "v{version}"
major_on_zero = false
allow_zero_version = true
build_command = """
    chmod +x scripts/generate_build_metadata.sh
    ./scripts/generate_build_metadata.sh
"""

[tool.semantic_release.branches.main]
match = "main"
prerelease = false

[tool.semantic_release.changelog]
mode = "update"
exclude_commit_patterns = [
    "^chore\\(release\\):",
    "^Merge",
    "^Bump version",
]

[tool.semantic_release.changelog.default_templates]
changelog_file = "CHANGELOG.md"
output_format = "md"

[tool.semantic_release.changelog.environment]
block_start_string = "{%"
block_end_string = "%}"
variable_start_string = "{{"
variable_end_string = "}}"
comment_start_string = "{#"
comment_end_string = "#}"
trim_blocks = false
lstrip_blocks = false
newline_sequence = "\n"
keep_trailing_newline = false
extensions = []
autoescape = false

[tool.semantic_release.commit_parser_options]
allowed_tags = [
    "feat",
    "fix",
    "docs",
    "style",
    "refactor",
    "perf",
    "test",
    "build",
    "ci",
    "chore",
]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]
default_bump_level = 0

[tool.semantic_release.remote]
name = "origin"
type = "github"
ignore_token_for_push = false

[tool.semantic_release.remote.token]
env = "GH_TOKEN"

[tool.semantic_release.publish]
dist_glob_patterns = []
upload_to_vcs_release = true
upload_to_pypi = false
upload_to_repository = false

# Ruff configuration
[tool.ruff]
line-length = 120
target-version = "py311"

[tool.ruff.lint]
# Enable Pyflakes (`F`), pycodestyle (`E`, `W`), isort (`I`), bandit (`S`), flake8-bugbear (`B`), and pylint (`PL`)
select = ["E", "F", "W", "I", "S", "B", "PL"]
ignore = [
    "E501",     # Line too long (handled by formatter)
    "S108",     # Hardcoded temp file (common pattern)
    "S105",     # Hardcoded password string (false positives with 'password' variable names)
    "S106",     # Hardcoded password func arg (false positives)
    "PLC0415",  # Import outside top-level (common in FastAPI/Celery)
    "PLR0913",  # Too many arguments
    "PLR0912",  # Too many branches
    "PLR0915",  # Too many statements
    "PLR0911",  # Too many return statements
    "PLR2004",  # Magic value comparison
    "PLW0603",  # Global statement
    "B008",     # Function call in default argument (FastAPI Depends pattern)
    "B904",     # Raise without from (overly strict for error handlers)
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101", "S110", "B017", "F841", "B007", "E402"]  # Allow assert, try-except-pass, assert-raises-exception, unused vars, unused loop vars, module imports not at top in tests

# pytest configuration
[tool.pytest.ini_options]
minversion = "7.0"
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
    "-v",
    "--strict-markers",
    "--strict-config",
    "--cov=app",
    "--cov-report=term-missing",
    "--cov-report=html",
    "--cov-branch",
    "--timeout=120",
]
markers = [
    "unit: Unit tests for individual functions/methods",
    "integration: Integration tests for API endpoints and workflows",
    "slow: Tests that take significant time to run",
    "security: Security-related tests",
    "requires_external: Tests requiring external services (OpenAI, Azure, etc.)",
    "requires_db: Tests requiring database",
    "requires_redis: Tests requiring Redis",
    "e2e: End-to-end tests with full infrastructure (requires Docker)",
    "requires_docker: Tests requiring Docker containers (testcontainers)",
]

# mypy configuration
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false
disallow_incomplete_defs = false
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = false
warn_no_return = true
warn_unreachable = false
strict_equality = true
ignore_missing_imports = true
# Disable error codes that produce widespread false positives with SQLAlchemy ORM,
# Pydantic, and other dynamic libraries used throughout the codebase.
# These would require major refactoring to resolve and are tracked for future work.
disable_error_code = [
    "assignment",       # SQLAlchemy Column[T] vs T assignments throughout ORM layer
    "arg-type",         # SQLAlchemy Column types passed where plain types expected
    "union-attr",       # Optional attribute access on ORM/Pydantic fields
    "call-overload",    # SQLAlchemy query builder overload resolution
    "return-value",     # Dynamic return types from ORM queries
    "attr-defined",     # Dynamic attributes on ORM models and API clients
    "no-any-return",    # Functions wrapping dynamic library calls
    "var-annotated",    # Complex inferred types from ORM queries
    "index",            # Indexing on dynamic ORM result types
    "operator",         # Operator usage on Optional/Column types
    "dict-item",        # Dict literal type inference with ORM fields
    "type-var",         # Type variable constraints with ORM generics
    "method-assign",    # Dynamic method assignment patterns
    "call-arg",         # Dynamic call signatures in framework code
]

# Stricter type-checking for utility modules
[[tool.mypy.overrides]]
module = "app.utils.*"
disallow_untyped_defs = true
disallow_incomplete_defs = true

# Coverage configuration
[tool.coverage.run]
source = ["app"]
omit = [
    "*/tests/*",
    "*/migrations/*",
    "*/__pycache__/*",
    "*/venv/*",
    "*/env/*",
]

[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == .__main__.:",
    "if TYPE_CHECKING:",
    "@abstractmethod",
]

# djLint configuration — HTML template linter for Jinja2 templates
# Enforces accessibility (WCAG 2.1 AA) and HTML best practices in CI.
# Run: djlint frontend/templates/ --profile jinja --lint
[tool.djlint]
profile = "jinja"
# Include only accessibility-critical and correctness rules for CI enforcement
include = "H005,H013,H016,H025,H026"
# Ignore rules that conflict with project patterns or produce excessive noise
ignore = "H006,H021,H023,H030,H031,H029,H036,T003,J018,J004,T028,H014,T001"
max_line_length = 120
