# AGENTS.md

## Build, Lint, and Test Commands

### Running Tests

```bash
# Run all tests
composer test
./phpunit

# Run all tests (Windows)
vendor\bin\phpunit

# Run a single test file
./phpunit tests/unit/HealthTest.php

# Run a single test method
./phpunit --filter testIsDefinedAppPath

# Run tests in a specific directory
./phpunit tests/unit/

# Run tests with coverage report
./phpunit --colors --coverage-text=tests/coverage.txt --coverage-html=tests/coverage/ -d memory_limit=1024m

# Generate HTML coverage report
./phpunit --coverage-html build/logs/html
```

### Dependencies

```bash
# Install dependencies
composer install

# Update dependencies
composer update
```

### CodeIgniter Spark Commands

```bash
# List available spark commands
php spark list

# Clear all caches
php spark cache:clear

# Run migrations
php spark migrate

# Rollback migrations
php spark migrate:rollback
```

## Code Style Guidelines

### General Principles

- Write clean, readable code without unnecessary comments
- Follow CodeIgniter 4 conventions and PSR-12 PHP standards
- Keep methods focused and under 100 lines when possible
- Avoid deep nesting (max 3-4 levels)

### Naming Conventions

| Element | Convention | Examples |
|---------|------------|----------|
| Classes | PascalCase | `ProductsModel`, `BaseController`, `UsersLogModel` |
| Files | Match class name | `ProductsModel.php`, `BaseController.php` |
| Methods | camelCase | `getProductAlias()`, `productslog_edit()` |
| Variables | camelCase | `$productid`, `$data`, `$results` |
| Properties | snake_case | `$table`, `$primaryKey`, `$allowedFields` |
| Constants | UPPER_SNAKE_CASE | `APPPATH`, `HOMEPATH` |
| Namespaces | PascalCase | `App\Controllers`, `App\Models`, `Config` |

### File Structure

```php
<?php

namespace App\Controllers;

use App\Models\ProductsModel;
use CodeIgniter\Controller;

class Products extends BaseController
{
    protected array $data;

    public function index()
    {
        // method body
    }
}
```

### Imports and Namespaces

- Use explicit imports for all external classes
- Namespace should reflect file location (`App\Controllers\*`, `App\Models\*`)
- Place namespace at top, followed by imports, then class definition
- Do not use fully qualified class names inline

```php
<?php

namespace App\Controllers;

use App\Models\ProductsModel;
use App\Models\ProductsLogModel;
use CodeIgniter\Controller;
```

### Indentation and Spacing

- Use tabs for indentation (4 spaces per tab is acceptable)
- No trailing whitespace
- One blank line between method definitions
- Space around operators: `$productid = $value;`
- No space between function name and parentheses: `func()` not `func ()`

### Arrays and Configuration

```php
// Short array syntax for simple arrays
protected array $data;

$this->data['productowners'] = ['S' => 'Summit', 'C' => 'Customer'];

// Multi-line array with proper alignment
protected $allowedFields = [
    'siteid',
    'productnumber',
    'productname',
    'catalogid',
];
```

### Control Structures

```php
// Single-line condition with braces
if ($productid !== null) {
    // code
}

// Multi-line conditions
if ($areaid !== '' || $producttypeid !== '') {
    $sql .= ' AND ';
}

// Ternary for simple conditions
$locationstartdate = ($this->request->getVar('locationstartdate') === '') 
    ? null 
    : $this->request->getVar('locationstartdate');
```

### Database Operations

- Use `$db = \Config\Database::connect();` for direct queries
- Use Query Builder when possible: `$db->table()->get()->getResultArray()`
- Always escape user input: `$db->escapeString($value)`
- Use parameterized queries to prevent SQL injection

```php
$builder = $db->table('products');
$builder->where('productid', $productid);
$query = $builder->get();
$results = $query->getResultArray();
```

### Controllers

- All controllers extend `BaseController`
- Use `$this->request->getMethod()` to check request type
- Use `$this->request->getVar()` or `$this->request->getPost()` for input
- Return views with `return view('view_name', $data);`
- Validate input with `$this->validate($rules)` before processing

```php
public function edit($productid = null)
{
    if ($this->request->getMethod() === 'POST') {
        $rules = [
            'productid' => 'required',
            'productnumber' => 'required',
        ];

        if ($this->validate($rules)) {
            // process form
        }
    }

    return view('products_edit', $data);
}
```

### Models

- All models extend `CodeIgniter\Model`
- Define `$table`, `$primaryKey`, `$allowedFields`
- Use `$useSoftDeletes` for soft delete functionality
- Define `$useTimestamps` if using created_at/updated_at

```php
<?php namespace App\Models;

use CodeIgniter\Model;

class ProductsModel extends Model
{
    protected $table = 'products';
    protected $primaryKey = 'productid';
    protected $allowedFields = ['siteid', 'productnumber', 'productname'];
}
```

### Error Handling

- Use CodeIgniter's built-in validation: `$this->validate($rules)`
- Return appropriate HTTP status codes in API controllers
- Use `try/catch` for operations that may throw exceptions
- Log errors using CodeIgniter's logging: `log_message('error', $message)`

```php
try {
    $productsModel = new ProductsModel();
    $productsModel->update($productid, $data['new_value']);
    return view('form_success', $data);
} catch (\Exception $e) {
    log_message('error', $e->getMessage());
    return view('error_page');
}
```

### Views

- Place views in `app/Views/` directory
- Use `.php` extension
- Access data via associative array: `$products`, `$data['products']`
- Use CodeIgniter's view parser or raw PHP

### API Responses

```php
// Return JSON response
return $this->response->setJSON([
    'status' => 'success',
    'data' => $data,
]);

// Error response
return $this->response->setJSON([
    'status' => 'error',
    'message' => 'No data found',
]);
```

### Testing

- Extend `CodeIgniter\Test\CIUnitTestCase` for all tests
- Test methods must start with `test` prefix
- Use `@internal` docblock for test classes

```php
<?php

use CodeIgniter\Test\CIUnitTestCase;

final class HealthTest extends CIUnitTestCase
{
    public function testIsDefinedAppPath(): void
    {
        $this->assertTrue(defined('APPPATH'));
    }
}
```

### PHP Requirements

- PHP 8.1 or higher
- Extensions required: `intl`, `mbstring`, `json`
- MySQLi database driver by default
