# Phase 1: Database Schema & Core Logic

## Overview
Establish the database foundation for enhanced fuel logs with new fields, validation rules, and core type system updates.

## Prerequisites
- Existing fuel logs feature (basic implementation)
- PostgreSQL database with current `fuel_logs` table
- Migration system functional

## Database Schema Changes

### New Fields to Add

```sql
-- Add these columns to fuel_logs table
ALTER TABLE fuel_logs ADD COLUMN trip_distance INTEGER; -- Alternative to odometer reading
ALTER TABLE fuel_logs ADD COLUMN fuel_type VARCHAR(20) NOT NULL DEFAULT 'gasoline';
ALTER TABLE fuel_logs ADD COLUMN fuel_grade VARCHAR(10);
ALTER TABLE fuel_logs ADD COLUMN fuel_units DECIMAL(8,3); -- Replaces gallons for metric support
ALTER TABLE fuel_logs ADD COLUMN cost_per_unit DECIMAL(6,3); -- Replaces price_per_gallon
ALTER TABLE fuel_logs ADD COLUMN location_data JSONB; -- Future Google Maps integration
ALTER TABLE fuel_logs ADD COLUMN date_time TIMESTAMP WITH TIME ZONE; -- Enhanced date/time

-- Add constraints
ALTER TABLE fuel_logs ADD CONSTRAINT fuel_type_check 
  CHECK (fuel_type IN ('gasoline', 'diesel', 'electric'));

-- Add conditional constraint: either trip_distance OR odometer_reading required
ALTER TABLE fuel_logs ADD CONSTRAINT distance_required_check
  CHECK ((trip_distance IS NOT NULL AND trip_distance > 0) OR (odometer_reading IS NOT NULL AND odometer_reading > 0));

-- Add indexes for performance
CREATE INDEX idx_fuel_logs_fuel_type ON fuel_logs(fuel_type);
CREATE INDEX idx_fuel_logs_date_time ON fuel_logs(date_time);
```

### Migration Strategy

#### Step 1: Additive Migration
**File**: `backend/src/features/fuel-logs/migrations/002_enhance_fuel_logs_schema.sql`

```sql
-- Migration: 002_enhance_fuel_logs_schema.sql
BEGIN;

-- Add new columns (nullable initially for data migration)
ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS trip_distance INTEGER;
ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS fuel_type VARCHAR(20);
ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS fuel_grade VARCHAR(10);
ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS fuel_units DECIMAL(8,3);
ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS cost_per_unit DECIMAL(6,3);
ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS location_data JSONB;
ALTER TABLE fuel_logs ADD COLUMN IF NOT EXISTS date_time TIMESTAMP WITH TIME ZONE;

-- Migrate existing data
UPDATE fuel_logs SET 
  fuel_type = 'gasoline',
  fuel_units = gallons,
  cost_per_unit = price_per_gallon,
  date_time = date::timestamp + interval '12 hours' -- Default to noon
WHERE fuel_type IS NULL;

-- Add constraints after data migration
ALTER TABLE fuel_logs ALTER COLUMN fuel_type SET NOT NULL;
ALTER TABLE fuel_logs ALTER COLUMN fuel_type SET DEFAULT 'gasoline';

-- Add check constraints
ALTER TABLE fuel_logs ADD CONSTRAINT fuel_type_check 
  CHECK (fuel_type IN ('gasoline', 'diesel', 'electric'));

-- Distance requirement constraint (either trip_distance OR odometer_reading)
ALTER TABLE fuel_logs ADD CONSTRAINT distance_required_check
  CHECK ((trip_distance IS NOT NULL AND trip_distance > 0) OR 
         (odometer_reading IS NOT NULL AND odometer_reading > 0));

-- Add performance indexes
CREATE INDEX IF NOT EXISTS idx_fuel_logs_fuel_type ON fuel_logs(fuel_type);
CREATE INDEX IF NOT EXISTS idx_fuel_logs_date_time ON fuel_logs(date_time);

COMMIT;
```

#### Step 2: Backward Compatibility Plan
- Keep existing `gallons` and `price_per_gallon` fields during transition
- Update application logic to use new fields preferentially
- Plan deprecation of old fields in future migration

### Data Validation Rules

#### Core Business Rules
1. **Distance Requirement**: Either `trip_distance` OR `odometer_reading` must be provided
2. **Fuel Type Validation**: Must be one of: 'gasoline', 'diesel', 'electric'
3. **Fuel Grade Validation**: Must match fuel type options
4. **Positive Values**: All numeric fields must be > 0
5. **DateTime**: Cannot be in the future

#### Fuel Grade Validation Logic
```sql
-- Fuel grade validation by type
CREATE OR REPLACE FUNCTION validate_fuel_grade()
RETURNS TRIGGER AS $$
BEGIN
  -- Gasoline grades
  IF NEW.fuel_type = 'gasoline' AND 
     NEW.fuel_grade NOT IN ('87', '88', '89', '91', '93') THEN
    RAISE EXCEPTION 'Invalid fuel grade % for gasoline', NEW.fuel_grade;
  END IF;
  
  -- Diesel grades  
  IF NEW.fuel_type = 'diesel' AND 
     NEW.fuel_grade NOT IN ('#1', '#2') THEN
    RAISE EXCEPTION 'Invalid fuel grade % for diesel', NEW.fuel_grade;
  END IF;
  
  -- Electric (no grades)
  IF NEW.fuel_type = 'electric' AND 
     NEW.fuel_grade IS NOT NULL THEN
    RAISE EXCEPTION 'Electric fuel type cannot have a grade';
  END IF;
  
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Create trigger
CREATE TRIGGER fuel_grade_validation_trigger
  BEFORE INSERT OR UPDATE ON fuel_logs
  FOR EACH ROW EXECUTE FUNCTION validate_fuel_grade();
```

## TypeScript Type System Updates

### New Core Types

**File**: `backend/src/features/fuel-logs/domain/fuel-logs.types.ts`

```typescript
// Fuel system enums
export enum FuelType {
  GASOLINE = 'gasoline',
  DIESEL = 'diesel',
  ELECTRIC = 'electric'
}

export enum GasolineFuelGrade {
  REGULAR_87 = '87',
  MIDGRADE_88 = '88',
  MIDGRADE_89 = '89',
  PREMIUM_91 = '91',
  PREMIUM_93 = '93'
}

export enum DieselFuelGrade {
  DIESEL_1 = '#1',
  DIESEL_2 = '#2'
}

export type FuelGrade = GasolineFuelGrade | DieselFuelGrade | null;

// Unit system types
export enum UnitSystem {
  IMPERIAL = 'imperial',
  METRIC = 'metric'
}

export interface UnitConversion {
  fuelUnits: string; // 'gallons' | 'liters'
  distanceUnits: string; // 'miles' | 'kilometers'
  efficiencyUnits: string; // 'mpg' | 'l/100km'
}

// Enhanced location data structure
export interface LocationData {
  address?: string;
  coordinates?: {
    latitude: number;
    longitude: number;
  };
  googlePlaceId?: string;
  stationName?: string;
  // Future: station prices, fuel availability
}

// Updated core FuelLog interface
export interface FuelLog {
  id: string;
  userId: string;
  vehicleId: string;
  dateTime: Date; // Enhanced from simple date
  
  // Distance tracking (either/or required)
  odometerReading?: number;
  tripDistance?: number;
  
  // Fuel system
  fuelType: FuelType;
  fuelGrade?: FuelGrade;
  fuelUnits: number; // Replaces gallons
  costPerUnit: number; // Replaces pricePerGallon
  totalCost: number; // Auto-calculated
  
  // Location (future Google Maps integration)
  locationData?: LocationData;
  
  // Legacy fields (maintain during transition)
  gallons?: number; // Deprecated
  pricePerGallon?: number; // Deprecated
  
  // Metadata
  notes?: string;
  mpg?: number; // Calculated efficiency
  createdAt: Date;
  updatedAt: Date;
}
```

### Request/Response Type Updates

```typescript
export interface CreateFuelLogRequest {
  vehicleId: string;
  dateTime: string; // ISO datetime string
  
  // Distance (either required)
  odometerReading?: number;
  tripDistance?: number;
  
  // Fuel system
  fuelType: FuelType;
  fuelGrade?: FuelGrade;
  fuelUnits: number;
  costPerUnit: number;
  // totalCost calculated automatically
  
  // Location
  locationData?: LocationData;
  notes?: string;
}

export interface UpdateFuelLogRequest {
  dateTime?: string;
  odometerReading?: number;
  tripDistance?: number;
  fuelType?: FuelType;
  fuelGrade?: FuelGrade;
  fuelUnits?: number;
  costPerUnit?: number;
  locationData?: LocationData;
  notes?: string;
}
```

## Core Validation Logic

### Business Rule Validation

**File**: `backend/src/features/fuel-logs/domain/fuel-logs.validation.ts`

```typescript
export class FuelLogValidation {
  
  static validateDistanceRequirement(data: CreateFuelLogRequest | UpdateFuelLogRequest): void {
    const hasOdometer = data.odometerReading && data.odometerReading > 0;
    const hasTripDistance = data.tripDistance && data.tripDistance > 0;
    
    if (!hasOdometer && !hasTripDistance) {
      throw new ValidationError('Either odometer reading or trip distance is required');
    }
    
    if (hasOdometer && hasTripDistance) {
      throw new ValidationError('Cannot specify both odometer reading and trip distance');
    }
  }
  
  static validateFuelGrade(fuelType: FuelType, fuelGrade?: FuelGrade): void {
    switch (fuelType) {
      case FuelType.GASOLINE:
        if (fuelGrade && !Object.values(GasolineFuelGrade).includes(fuelGrade as GasolineFuelGrade)) {
          throw new ValidationError(`Invalid gasoline grade: ${fuelGrade}`);
        }
        break;
        
      case FuelType.DIESEL:
        if (fuelGrade && !Object.values(DieselFuelGrade).includes(fuelGrade as DieselFuelGrade)) {
          throw new ValidationError(`Invalid diesel grade: ${fuelGrade}`);
        }
        break;
        
      case FuelType.ELECTRIC:
        if (fuelGrade) {
          throw new ValidationError('Electric vehicles cannot have fuel grades');
        }
        break;
    }
  }
  
  static validatePositiveValues(data: CreateFuelLogRequest | UpdateFuelLogRequest): void {
    if (data.fuelUnits && data.fuelUnits <= 0) {
      throw new ValidationError('Fuel units must be positive');
    }
    
    if (data.costPerUnit && data.costPerUnit <= 0) {
      throw new ValidationError('Cost per unit must be positive');
    }
    
    if (data.odometerReading && data.odometerReading <= 0) {
      throw new ValidationError('Odometer reading must be positive');
    }
    
    if (data.tripDistance && data.tripDistance <= 0) {
      throw new ValidationError('Trip distance must be positive');
    }
  }
  
  static validateDateTime(dateTime: string): void {
    const date = new Date(dateTime);
    const now = new Date();
    
    if (date > now) {
      throw new ValidationError('Cannot create fuel logs in the future');
    }
  }
}
```

## Implementation Tasks

### Database Tasks
1. ✅ Create migration file `002_enhance_fuel_logs_schema.sql`
2. ✅ Add new columns with appropriate types
3. ✅ Migrate existing data to new schema
4. ✅ Add database constraints and triggers
5. ✅ Create performance indexes

### Type System Tasks  
1. ✅ Define fuel system enums
2. ✅ Create unit system types
3. ✅ Update core FuelLog interface
4. ✅ Update request/response interfaces
5. ✅ Add location data structure

### Validation Tasks
1. ✅ Create validation utility class
2. ✅ Implement distance requirement validation
3. ✅ Implement fuel grade validation
4. ✅ Add positive value checks
5. ✅ Add datetime validation

## Testing Requirements

### Database Testing
```sql
-- Test distance requirement constraint
INSERT INTO fuel_logs (...) -- Should fail without distance
INSERT INTO fuel_logs (trip_distance = 150, ...) -- Should succeed
INSERT INTO fuel_logs (odometer_reading = 50000, ...) -- Should succeed
INSERT INTO fuel_logs (trip_distance = 150, odometer_reading = 50000, ...) -- Should fail

-- Test fuel type/grade validation
INSERT INTO fuel_logs (fuel_type = 'gasoline', fuel_grade = '87', ...) -- Should succeed
INSERT INTO fuel_logs (fuel_type = 'gasoline', fuel_grade = '#1', ...) -- Should fail
INSERT INTO fuel_logs (fuel_type = 'electric', fuel_grade = '87', ...) -- Should fail
```

### Unit Tests Required
- Validation logic for all business rules
- Type conversion utilities
- Migration data integrity
- Constraint enforcement

## Success Criteria

### Phase 1 Complete When:
- ✅ Database migration runs successfully
- ✅ All new fields available with proper types
- ✅ Existing data migrated and preserved
- ✅ Database constraints enforce business rules
- ✅ TypeScript interfaces updated and compiling
- ✅ Core validation logic implemented and tested
- ✅ No breaking changes to existing functionality

### Ready for Phase 2 When:
- All database changes deployed and tested
- Type system fully updated
- Core validation passes all tests
- Existing fuel logs feature still functional

---

**Next Phase**: [Phase 2 - Enhanced Business Logic](FUEL-LOGS-PHASE-2.md)