# Database Schema

## Overview
The database uses PostgreSQL with SQLAlchemy ORM. All tables are auto-created from models.

## Tables

### users
User accounts and profile information.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | User identifier |
| email | VARCHAR | UNIQUE, INDEXED | User email |
| username | VARCHAR | UNIQUE, INDEXED | Username (optional, for login) |
| hashed_password | VARCHAR | NOT NULL | Bcrypt hashed password |
| full_name | VARCHAR | NOT NULL | User's full name |
| phone | VARCHAR | UNIQUE, INDEXED | Phone number (optional, for login) |
| address | VARCHAR | NULLABLE | Street address |
| city | VARCHAR | NULLABLE | City |
| postal_code | VARCHAR | NULLABLE | Postal/ZIP code |
| country | VARCHAR | NULLABLE | Country |
| is_active | BOOLEAN | DEFAULT TRUE | Account status |
| is_admin | BOOLEAN | DEFAULT FALSE | Admin privileges |
| must_change_password | BOOLEAN | DEFAULT FALSE | Force password change |
| password_reset_pin | VARCHAR | NULLABLE | Password reset PIN |
| pin_expires_at | DATETIME | NULLABLE | PIN expiration time |
| created_at | DATETIME | DEFAULT NOW() | Account creation date |

**Login Options:** Users can login with email, username, or phone number

### categories
Product categories.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Category identifier |
| name | VARCHAR | UNIQUE, INDEXED | Category name |
| slug | VARCHAR | UNIQUE, INDEXED | URL-friendly slug |
| description | VARCHAR | NULLABLE | Category description |

**Predefined Categories:**
- Shoes
- Shirts
- Pants
- Hats
- Accessories

### products
Product inventory and details.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Product identifier |
| name | VARCHAR | INDEXED | Product name |
| description | TEXT | NOT NULL | Product description |
| price | FLOAT | NOT NULL | Regular price |
| discount_price | FLOAT | NULLABLE | Sale price if on sale |
| category_id | INTEGER | FOREIGN KEY | Reference to category |
| gender | VARCHAR | NOT NULL | men/women |
| brand | VARCHAR | NOT NULL | Product brand |
| sizes | JSON | NOT NULL | Array of available sizes |
| colors | JSON | NOT NULL | Array of available colors |
| stock | INTEGER | DEFAULT 0 | Quantity available |
| images | JSON | NOT NULL | Array of image URLs |
| is_featured | BOOLEAN | DEFAULT FALSE | Featured product flag |
| is_on_sale | BOOLEAN | DEFAULT FALSE | Sale flag |
| created_at | DATETIME | DEFAULT NOW() | Product added date |

**indexes:**
- category_id (FOREIGN KEY)

### cart
User shopping carts.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Cart identifier |
| user_id | INTEGER | FOREIGN KEY UNIQUE | Reference to user |
| created_at | DATETIME | DEFAULT NOW() | Cart creation date |

**Relationships:**
- One cart per user
- Can contain multiple cart_items

### cart_items
Items in shopping carts.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Cart item identifier |
| cart_id | INTEGER | FOREIGN KEY | Reference to cart |
| product_id | INTEGER | FOREIGN KEY | Reference to product |
| quantity | INTEGER | DEFAULT 1 | Item quantity |
| size | VARCHAR | NULLABLE | Selected size |
| color | VARCHAR | NULLABLE | Selected color |

**Relationships:**
- Belongs to cart
- References product

### orders
Customer orders.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Order identifier |
| user_id | INTEGER | FOREIGN KEY | Reference to user |
| order_number | VARCHAR | UNIQUE, INDEXED | Human-readable order # |
| status | VARCHAR | DEFAULT 'pending' | pending/paid/shipped/delivered |
| total_amount | FLOAT | NOT NULL | Order total |
| shipping_address | VARCHAR | NOT NULL | Shipping street |
| shipping_city | VARCHAR | NOT NULL | Shipping city |
| shipping_postal_code | VARCHAR | NOT NULL | Shipping postal code |
| shipping_country | VARCHAR | NOT NULL | Shipping country |
| created_at | DATETIME | DEFAULT NOW() | Order creation date |
| updated_at | DATETIME | DEFAULT NOW() | Last update date |

**Relationships:**
- Belongs to user
- Can contain multiple order_items

### order_items
Items in orders.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Order item identifier |
| order_id | INTEGER | FOREIGN KEY | Reference to order |
| product_id | INTEGER | FOREIGN KEY | Reference to product |
| quantity | INTEGER | NOT NULL | Item quantity |
| price | FLOAT | NOT NULL | Price at purchase time |
| size | VARCHAR | NULLABLE | Selected size |
| color | VARCHAR | NULLABLE | Selected color |

**Note:** Price is stored to preserve historical data

### wishlist
User wishlists.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Wishlist item identifier |
| user_id | INTEGER | FOREIGN KEY | Reference to user |
| product_id | INTEGER | FOREIGN KEY | Reference to product |
| created_at | DATETIME | DEFAULT NOW() | Added to wishlist date |

**Constraint:** UNIQUE(user_id, product_id) - one entry per user-product pair

### contact_messages
Contact form submissions.

| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY | Message identifier |
| full_name | VARCHAR | NOT NULL | Sender full name |
| email | VARCHAR | NOT NULL | Sender email |
| phone | VARCHAR | NULLABLE | Sender phone (optional) |
| subject | VARCHAR | NOT NULL | Message subject |
| message | TEXT | NOT NULL | Message content |
| created_at | DATETIME | DEFAULT NOW() | Submission date |
| is_read | BOOLEAN | DEFAULT FALSE | Admin read status |
| status | VARCHAR | DEFAULT 'new' | Status: new/read/replied |
| admin_notes | TEXT | NULLABLE | Internal admin notes |

**Constraints:** CHECK (status IN ('new', 'read', 'replied'))  
**Indexes:** status, is_read, created_at

## Relationships Diagram

```
users
  ├─ 1:1 ─→ cart
  │           ├─ 1:many ─→ cart_items
  │                         └─ many:1 ─→ products
  ├─ 1:many ─→ orders
  │             ├─ 1:many ─→ order_items
  │                           └─ many:1 ─→ products
  └─ many:many ─→ products (via wishlist)

categories
  └─ 1:many ─→ products

contact_messages
  (standalone)
```

## Sample Data

The seed script includes:

**Categories:** 5
- Shoes, Shirts, Pants, Hats, Accessories

**Products:** 15+
- emphasis on shoes (Nike, Adidas, Cole Haan, Salomon, etc.)
- Mix of men's and women's items
- Various price points ($19.99 - $189.99)
- Sale and featured items

**Users:** 2
- user@example.com (password: password123)
- jane@example.com (password: password123)

## Indexing Strategy

Indexed columns for performance:
- users.email (UNIQUE)
- users.username (UNIQUE, partial index on non-null)
- users.phone (UNIQUE, partial index on non-null)
- categories.name (UNIQUE)
- categories.slug (UNIQUE)
- products.name
- products.category_id
- orders.order_number (UNIQUE)
- orders.user_id
- contact_messages.status
- contact_messages.is_read
- contact_messages.created_at

## Backup and Restore

### Backup Database
```bash
pg_dump -U postgres ecommerce_db > backup.sql
```

### Restore Database
```bash
psql -U postgres ecommerce_db < backup.sql
```

## Connection Pooling

In production, consider using a connection pool. SQLAlchemy is configured in `database.py`.

## Migration Notes

If modifying models:
1. Update the model in `app/models/`
2. Tables auto-create via `Base.metadata.create_all()`
3. For existing databases, manually ALTER TABLE

## Performance Optimization

- Indexes on frequently queried columns
- JSON columns for flexible attributes
- Pagination implemented in list queries
- EAGER loading relationships where needed

## Data Validation

- Pydantic schemas validate all input
- Email validation via EmailStr
- Price and quantity constraints
- Product stock management

## Time Zone

All timestamps are stored in UTC (datetime.utcnow())
