[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "skincare-filter"
version = "0.1.0"
description = "Skincare Allergy Filter - Cross-reference product ingredients against personal allergens"
readme = "README.md"
requires-python = ">=3.13"
license = {text = "MIT"}
authors = [
    {name = "RJChoe", email = "your-email@example.com"}
]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Framework :: Django :: 6.0",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.13",
]
dependencies = [
    "asgiref==3.11.0",
    "dj-database-url==2.2.0",
    "django==6.0.2",
    "django-cleanup>=9.0.0",
    "django-environ>=0.12.0",
    "pillow>=12.1.1",
    "psycopg2-binary==2.9.11",
    "sqlparse==0.5.5",
    "typing-extensions==4.15.0",
    "tzdata==2025.3",
]

[project.urls]
Homepage = "https://github.com/RJChoe/Skincare-Filter-Web-Application"
Repository = "https://github.com/RJChoe/Skincare-Filter-Web-Application"
Issues = "https://github.com/RJChoe/Skincare-Filter-Web-Application/issues"

# ============================================================================
# uv Configuration
# ============================================================================
# Python version pinning: .python-version pins local development to 3.13 for
# consistency across the team and with CI/CD. However, requires-python = ">=3.13"
# remains flexible to allow the package to work with future Python versions.
[tool.uv]
# Prefer uv-managed Python installations for reproducibility
python-preference = "only-managed"

# Automatically download Python if the required version is missing
python-downloads = "automatic"

# Compile bytecode for faster imports
compile-bytecode = true

# Resolve to the lowest compatible version for direct dependencies (conservative, security-friendly)
resolution = "lowest-direct"

# Only use prereleases when necessary (no stable version available)
prerelease = "if-necessary"

# Environment markers for cross-platform support
environments = ["platform_system == 'Windows'", "platform_system != 'Windows'"]

[[tool.uv.index]]
url = "https://pkgs.safetycli.com/repository/public/pypi/simple/"
default = true

# ============================================================================
# PEP 735 Dependency Groups
# ============================================================================
[dependency-groups]
# Testing tools and frameworks
test = [
    "pytest==9.0.2",
    "pytest-cov==7.0.0",
    "pytest-django==4.11.1",
    "coverage==7.13.0",
    "pluggy==1.6.0",
    "iniconfig==2.3.0",
    "psutil==6.0.0",
]

# Linting and code formatting tools
lint = [
    "ruff==0.14.10",
    "pre-commit==4.5.1",
    "identify==2.6.15",
    "nodeenv==1.10.0",
    "cfgv==3.5.0",
    "pathspec==0.12.1",
]

# Type checking tools
type-check = [
    "mypy==1.19.1",
    "mypy-extensions==1.1.0",
    "django-stubs==5.2.8",
    "django-stubs-ext==5.2.8",
    "types-pyyaml==6.0.12.20250915",
    "typing-inspection==0.4.2",
    "types-psycopg2>=2.9.0",
]

# Security scanning tools
security = [
    "bandit==1.8.0",
    "safety==3.7.0",
    "stevedore==5.6.0",
    # safety transitive deps
    "typer==0.20.1",
    "click==8.3.1",
    "rich==14.2.0",
    "shellingham==1.5.4",
]

# Full development environment (includes all groups + additional dev tools)
dev = [
    {include-group = "test"},
    {include-group = "lint"},
    {include-group = "type-check"},
    {include-group = "security"},
    # Django development tools
    "django-debug-toolbar==6.1.0",
    # YAML & config parsing (used by safety internals; pin for reproducibility)
    "pyyaml==6.0.3",
    # Build & packaging tools
    "setuptools==80.9.0",
    "packaging==25.0",
    "filelock>=3.16.1",
    "platformdirs==4.5.1",
    # Utilities
    "pygments==2.19.2",
    "pytest-randomly>=4.0.1",
]

# ============================================================================
# Ruff Configuration
# ============================================================================
[tool.ruff]
line-length = 88
target-version = "py313"
exclude = [
    ".git",
    "__pycache__",
    ".venv",
    "venv",
    "migrations",
    ".pytest_cache",
    "htmlcov",
    ".tox",
    "build",
    "dist",
]

[tool.ruff.lint]
select = [
    "PTH", # flake8-use-pathlib linter
    "E",   # pycodestyle errors
    "F",   # pyflakes
    "W",   # pycodestyle warnings
    "I",   # isort
    "DJ",  # Django
    "UP",  # pyupgrade
    "B",   # flake8-bugbear
    "C4",  # flake8-comprehensions
    "S",   # flake8-bandit (security)
    "T20", # flake8-print
    ##IMPLEMENT LATER "D",   # pydocstyle
]
ignore = [
    "E203",  # Whitespace before ':' (conflicts with formatters)
    "E501",  # Line too long (handled by formatter)
    "S101",  # Use of assert (acceptable in tests)
    "S105",  # Possible hardcoded password (false positives in Django settings)
    "S106",  # Possible hardcoded password (false positives in Django settings)
    "D203",  # 1 blank line required before class docstring (conflicts with D211)
    "D213",  # Multi-line docstring summary should start at second line (conflicts with D212)
## Docstring requirements (ENABLE these WHEN READY to write comprehensive docs)
    "D100",  # Missing module docstring
    "D104",  # Missing package docstring
    "D107",  # Missing __init__ docstring
]

[tool.ruff.lint.per-file-ignores]
"**/migrations/*" = ["DJ"]
"**/tests/*" = ["S101", "T20", "D100", "D101", "D102", "D103", "D104"]
"**/test_*.py" = ["S101", "T20", "D100", "D101", "D102", "D103", "D104"]
"**/__init__.py" = ["D100", "D104"]
"manage.py" = ["T20", "D100", "D103"]
"**/settings*.py" = ["S105", "S106", "D100", "D104"]
"**/apps.py" = ["F401", "D100", "D104"]
"**/signals.py" = ["F401"]
".github/scripts/*.py" = ["T20"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"
skip-magic-trailing-comma = false

[tool.ruff.lint.isort]
known-first-party = ["skincare_project", "allergies", "users"]
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]

# ============================================================================
# Mypy Configuration
# ============================================================================
[tool.mypy]
python_version = "3.13"
plugins = ["mypy_django_plugin.main"]
files = ["allergies", "users", "skincare_project"]
exclude = [".*?/migrations/.*", ".*?/tests/.*", "manage.py"]
strict = false
warn_return_any = true
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
disallow_untyped_defs = false
disallow_any_unimported = false
no_implicit_optional = true
check_untyped_defs = true
show_error_codes = true
pretty = true
ignore_missing_imports = true
strict_optional = true

## strict = true
## Optional: Enable mypy strict mode to more
## aggressively catch invalid settings access

# Django-specific settings
[tool.django-stubs]
django_settings_module = "skincare_project.settings"

[[tool.mypy.overrides]]
module = [
    "*.migrations.*",
    "manage",
]
ignore_errors = true

# ============================================================================
# Pytest Configuration
# ============================================================================
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "skincare_project.settings"
log_level = "WARNING" # suppress INFO/DEBUG noise in non-logging tests
python_files = ["tests.py", "test_*.py", "*_tests.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
testpaths = ["allergies/tests", "users/tests.py"]
addopts = [
    "--cov=allergies",
    "--cov=users",
    "--cov=skincare_project",
    "--cov-report=term-missing",
    "--cov-report=xml",
    "-ra",
    "--strict-markers",
    "--strict-config",
    "--tb=short",
]
markers = [
    "integration: Integration tests (slower)",
    "slow: Slow tests that take more than 1 second",
    "unit: Fast unit tests",
]
filterwarnings = [
    "error",
    "ignore::DeprecationWarning",
    "ignore::PendingDeprecationWarning",
]

# ============================================================================
# Coverage Configuration
# ============================================================================
[tool.coverage.run]
branch = true
source = ["allergies", "users", "skincare_project"]
omit = [
    "**/migrations/*",
    "**/tests/*",
    "**/__pycache__/*",
    "**/venv/*",
    "**/.venv/*",
    "manage.py",
    "**/wsgi.py",
    "**/asgi.py",
    "**/settings.py",
    "**/apps.py",
    "**/conftest.py",
    "**/check.py",
    "**/htmlcov/*",
    "**/dist/*",
    "**/build/*",
]

[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
fail_under = 75
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "def __str__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == .__main__.:",
    "if TYPE_CHECKING:",
    "@(abc\\.)?abstractmethod",
    "if settings.DEBUG:",
    "class Meta",
]

[tool.coverage.html]
directory = "htmlcov"

[tool.hatch.build.targets.wheel]
packages = ["allergies", "users", "skincare_project"]

[tool.deptry]
extend_exclude = ["tests"] # Ignore the tests directory for production dependency checks
