 # Implementation Plan: Visceral Fat Tracking

## Overview

This plan implements visceral fat tracking for the Albi health tracker app. The implementation follows the architecture's four-layer dependency order: Data → Logic → State → UI. Each task builds on the previous, ending with full integration and wiring. Property-based tests validate correctness properties defined in the design.

## Tasks

- [x] 1. Data layer — models, constants, and database migration
  - [x] 1.1 Create the WaistEntry model
    - Create `lib/models/waist_entry.dart` extending `HealthData`
    - Implement fields: `waistCm` (double), `originalUnit` (String)
    - Implement `factory WaistEntry.fromUserInput` with unit conversion to cm
    - Implement `factory WaistEntry.fromJson` and `toJson` for database serialization
    - Implement `copyWith`, `isValid` (30–300 cm range), `waistInOriginalUnit` getter
    - Throw `ArgumentError` for unsupported units (not cm, mm, or inches)
    - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 3.1, 3.4_

  - [x] 1.2 Create the UserProfile model
    - Create `lib/models/user_profile.dart` (not extending HealthData)
    - Implement fields: `heightValue` (double?), `heightUnit` (String?), `gender` (String?)
    - Implement `heightCm` getter that converts inches to cm when needed
    - Implement `hasHeight` and `hasGender` convenience getters
    - Implement `fromJson` / `toJson` for database serialization
    - _Requirements: 1.1, 1.2, 1.5_

  - [x] 1.3 Update AppConstants for database v2 and new table name
    - Change `databaseVersion` from `1` to `2` in `lib/utils/constants.dart`
    - Add `waistEntriesTable = 'waist_entries'` constant
    - Add `userProfileTable = 'user_profile'` constant
    - _Requirements: 9.1, 9.4_

  - [x] 1.4 Implement database migration v1→v2 and CRUD methods
    - In `lib/services/database_service.dart`, update `_onUpgrade` to handle `oldVersion < 2`
    - Create `user_profile` table (single-row, id CHECK = 1) and `waist_entries` table with index in a transaction
    - Update `_createTables` (onCreate) to create all five tables on fresh install
    - Add `getUserProfile()` and `saveUserProfile(UserProfile)` methods (upsert pattern with INSERT OR REPLACE)
    - Add full CRUD for waist entries: `insertWaistEntry`, `getWaistEntries`, `getWaistEntry`, `updateWaistEntry`, `deleteWaistEntry`, `getLatestWaistEntry`, `findDuplicateWaistEntries`
    - _Requirements: 1.3, 1.4, 1.5, 1.6, 2.7, 2.8, 9.1, 9.2, 9.3, 9.4_

- [x] 2. Logic layer — unit converter, risk assessor, chart data service
  - [x] 2.1 Create WaistUnitConverter utility
    - Create `lib/utils/waist_unit_converter.dart` as a static utility class
    - Implement constants: `mmToCm = 0.1`, `inchesToCm = 2.54`, `cmToMm = 10.0`, `cmToInches = 1/2.54`
    - Implement `toCm(double value, String unit)` and `fromCm(double cm, String unit)`
    - Implement `isValidUnit(String unit)` and `isValidWaistCm(double cm)` for range [30, 300]
    - Implement `formatWaist(double value, String unit)` with 1 decimal place
    - Throw `ArgumentError` for unsupported units
    - _Requirements: 3.1, 3.2, 3.3, 3.4_

  - [x] 2.2 Create RiskAssessor utility
    - Create `lib/utils/risk_assessor.dart` with enums `WaistRiskLevel`, `RatioRiskLevel` and result classes
    - Implement `assessWaistRisk(waistCm, gender)` — male >102 cm = increased risk, female >88 cm = increased risk, null gender = cannotAssess
    - Implement `assessWaistToHeightRatio(waistCm, heightCm)` — ratio ≥0.5 = increased risk, null height = cannotAssess, rounds to 2 decimal places
    - Keep class pure (no Flutter/state dependencies) for easy testing
    - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 5.1, 5.2, 5.3, 5.4, 5.6_

  - [x] 2.3 Extend ChartDataService with waist chart data method
    - Add `getWaistChartData({required ChartTimePeriod timePeriod, required String displayUnit, bool includeTrend})` to `lib/services/chart_data_service.dart`
    - Query waist entries from DatabaseService, convert to ChartDataPoints using WaistUnitConverter
    - Reuse existing `_calculateStatistics` and `_calculateTrendLine` helper methods
    - Return `ChartData` with points, trendLine, statistics, and unit
    - _Requirements: 7.1, 7.4_

- [x] 3. Checkpoint — Verify data and logic layers compile
  - Ensure all tests pass, ask the user if questions arise.

- [x] 4. State layer — BLoC events, states, and handlers
  - [x] 4.1 Add waist and profile events to HealthDataBloc
    - Add `AddWaistEntryEvent`, `LoadWaistEntriesEvent`, `SaveUserProfileEvent`, `LoadUserProfileEvent` to `lib/bloc/health_data_event.dart`
    - Follow existing event patterns (extend `HealthDataEvent`, use Equatable)
    - _Requirements: 6.6, 6.7_

  - [x] 4.2 Extend HealthDataLoaded state with waist and profile fields
    - Add `WaistEntry? latestWaist` and `UserProfile? userProfile` fields to `HealthDataLoaded` in `lib/bloc/health_data_state.dart`
    - Update `copyWith`, `props`, and `hasData` getter
    - _Requirements: 1.5, 6.6_

  - [x] 4.3 Implement BLoC event handlers for waist and profile operations
    - In `lib/bloc/health_data_bloc.dart`, register handlers for the new events
    - `on<AddWaistEntryEvent>`: insert via DatabaseService, emit success then reload
    - `on<LoadWaistEntriesEvent>`: query entries, emit loaded state
    - `on<SaveUserProfileEvent>`: save via DatabaseService, emit success then reload
    - `on<LoadUserProfileEvent>`: fetch profile, emit loaded state with profile
    - Update `LoadRecentEntriesEvent` handler to also load `latestWaist` and `userProfile`
    - Handle errors by emitting `HealthDataError.database`
    - _Requirements: 6.6, 6.7_

- [x] 5. UI layer — screens, widgets, and navigation
  - [x] 5.1 Create VisceralFatTipsWidget
    - Create `lib/widgets/visceral_fat_tips_widget.dart` as a stateless widget
    - Display 6 tips + 1 disclaimer in fixed order per Requirement 10
    - Make disclaimer visually distinct (different background/styling)
    - Support both inline usage (entry screen) and dialog usage (chart screen)
    - _Requirements: 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9_

  - [x] 5.2 Create TrackVisceralFatScreen
    - Create `lib/screens/track_visceral_fat_screen.dart` following AddWeightScreen patterns
    - Numeric input for waist circumference with unit selector (cm/mm/inches)
    - Date/time picker defaulting to now, optional notes field (maxLength 500)
    - Instructional note about measuring at navel level
    - Conditional dismissible prompts for height/gender when not set in profile
    - Inline VisceralFatTipsWidget below the form
    - Save button disabled until valid; on save dispatch `AddWaistEntryEvent`, show success snackbar, navigate back
    - On error state, show error message, preserve entered data
    - Input validation: range checks, future timestamp rejection, non-numeric rejection
    - _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 6.10, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7_

  - [x] 5.3 Create WaistChartScreen
    - Create `lib/screens/waist_chart_screen.dart` following BloodPressureChartScreen patterns
    - ChartDateSelector for period selection (1 month, 3 months, all time, custom)
    - Dual-axis line chart: waist circumference (left axis), waist-to-height ratio (right axis, conditional on height)
    - Statistics card: average, count, change, trend, min, max, range in user's unit
    - Tips button opening popup dialog with VisceralFatTipsWidget
    - Loading, error (with retry), and empty states
    - Hide ratio axis when height not available
    - _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8_

  - [x] 5.4 Integrate navigation — home screen quick action and charts screen card
    - In `lib/screens/home_screen.dart`, replace 4th quick action ("View Charts") with "Track Visceral Fat" linking to TrackVisceralFatScreen
    - In `lib/screens/charts_screen.dart`, add "Waist Circumference" navigation card between "Mood Patterns" and Quick Stats card, linking to WaistChartScreen
    - Add necessary imports for new screens
    - _Requirements: 8.1, 8.2, 8.3, 8.4_

- [~] 6. Checkpoint — Full integration verification
  - Ensure all tests pass, ask the user if questions arise.

- [x] 7. Property-based tests
  - [x]* 7.1 Write property test for user profile round-trip
    - **Property 1: User Profile Save/Load Round-Trip**
    - **Validates: Requirements 1.1, 1.4, 1.5**
    - Generate random valid UserProfile instances (height 50–275 cm equivalent, unit cm/inches, gender male/female/null)
    - Save via DatabaseService.saveUserProfile, load via getUserProfile, assert field equality

  - [x]* 7.2 Write property test for waist entry storage round-trip
    - **Property 2: Waist Entry Storage Round-Trip**
    - **Validates: Requirements 2.1, 2.3, 2.4, 2.5**
    - Generate random valid WaistEntry instances (waist_cm 30–300, unit cm/mm/inches, past timestamp, notes ≤500 or null)
    - Insert via DatabaseService.insertWaistEntry, retrieve by ID, assert all fields identical

  - [x]* 7.3 Write property test for waist unit conversion round-trip
    - **Property 3: Waist Unit Conversion Round-Trip**
    - **Validates: Requirements 3.1, 3.2, 3.3**
    - Generate random valid measurements (cm-equivalent in [30, 300]) with any supported unit
    - Convert toCm then fromCm, assert result within 0.01 of original

  - [x]* 7.4 Write property test for invalid unit rejection
    - **Property 4: Invalid Unit Rejection**
    - **Validates: Requirements 3.4**
    - Generate random strings not in {"cm", "mm", "inches"}
    - Assert WaistUnitConverter.toCm throws ArgumentError

  - [x]* 7.5 Write property test for waist risk classification
    - **Property 5: Waist Risk Classification Correctness**
    - **Validates: Requirements 4.1, 4.2, 4.3, 4.4**
    - Generate random waist (30–300 cm) and gender (male/female)
    - Assert: male >102 → increased risk, ≤102 → normal; female >88 → increased risk, ≤88 → normal

  - [x]* 7.6 Write property test for ratio mathematical correctness
    - **Property 6: Waist-to-Height Ratio Mathematical Correctness**
    - **Validates: Requirements 5.1, 5.6**
    - Generate random waist [30,300] and height [50,275] in cm
    - Assert computed ratio equals waistCm/heightCm rounded to 2 decimal places

  - [x]* 7.7 Write property test for ratio risk classification
    - **Property 7: Ratio Risk Classification Correctness**
    - **Validates: Requirements 5.2, 5.3**
    - Generate random waist [30,300] and height [50,275]
    - Assert: ratio <0.5 → healthy, ≥0.5 → increased risk

  - [x]* 7.8 Write property test for waist range validation
    - **Property 8: Waist Range Validation Correctness**
    - **Validates: Requirements 2.6, 11.1, 11.2**
    - Generate random values and units, convert to cm
    - Assert: accepted iff cm-equivalent in [30.0, 300.0], rejected otherwise

  - [x]* 7.9 Write property test for height range validation
    - **Property 9: Height Range Validation Correctness**
    - **Validates: Requirements 11.3, 11.4**
    - Generate random height values and units (cm/inches)
    - Assert: accepted iff cm-equivalent in [50.0, 275.0], rejected otherwise

  - [x]* 7.10 Write property test for future timestamp rejection
    - **Property 10: Future Timestamp Rejection**
    - **Validates: Requirements 11.5**
    - Generate random DateTimes after DateTime.now()
    - Assert validation rejects and prevents saving

  - [x]* 7.11 Write property test for non-numeric input rejection
    - **Property 11: Non-Numeric Input Rejection**
    - **Validates: Requirements 11.6**
    - Generate random non-numeric strings (empty, whitespace, alphabetic, special chars)
    - Assert validation rejects input

  - [x]* 7.12 Write property test for chart statistics correctness
    - **Property 12: Chart Statistics Mathematical Correctness**
    - **Validates: Requirements 7.4**
    - Generate random non-empty lists of waist entries
    - Assert: average = arithmetic mean, min = smallest, max = largest, range = max-min, count = length, change = last-first

  - [x]* 7.13 Write property test for migration data preservation
    - **Property 13: Migration Data Preservation**
    - **Validates: Requirements 9.1, 9.2**
    - Seed v1 database with random weight, blood pressure, and mood entries
    - Run migration to v2
    - Assert all existing entry row counts and column values unchanged

- [~] 8. Final checkpoint — Ensure all tests pass
  - Ensure all tests pass, ask the user if questions arise.

## Notes

- Tasks marked with `*` are optional and can be skipped for faster MVP
- Each task references specific requirements for traceability
- Checkpoints ensure incremental validation
- Property tests validate universal correctness properties from the design document
- Unit tests validate specific examples and edge cases
- The implementation language is Dart (Flutter), matching the design document
- Use `glados` or equivalent Dart PBT library for property-based tests (minimum 100 iterations)

## Task Dependency Graph

```json
{
  "waves": [
    { "id": 0, "tasks": ["1.1", "1.2", "1.3"] },
    { "id": 1, "tasks": ["1.4"] },
    { "id": 2, "tasks": ["2.1", "2.2"] },
    { "id": 3, "tasks": ["2.3", "4.1", "4.2"] },
    { "id": 4, "tasks": ["4.3"] },
    { "id": 5, "tasks": ["5.1"] },
    { "id": 6, "tasks": ["5.2", "5.3"] },
    { "id": 7, "tasks": ["5.4"] },
    { "id": 8, "tasks": ["7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "7.7", "7.8", "7.9", "7.10", "7.11", "7.12", "7.13"] }
  ]
}
```
