# Refactor: extract shared TileCsvWriter

**Task**: AZ-368_refactor_extract_tile_csv_writer
**Name**: Shared CSV writer for tile lists
**Description**: Extract `TileCsvWriter` from `RegionService` and `RouteProcessingService`.
**Complexity**: 2 points
**Dependencies**: None
**Component**: Common + Services.RegionProcessing + Services.RouteManagement
**Tracker**: AZ-368
**Epic**: AZ-350

## Problem

`SatelliteProvider.Services.RegionProcessing/RegionService.cs:323-334` and `SatelliteProvider.Services.RouteManagement/RouteProcessingService.cs:388-404` both write the same CSV header (`latitude,longitude,file_path`) with the same ordering rule (`OrderByDescending(t.Latitude).ThenBy(t.Longitude)`) and the same `F6` numeric format. Two near-identical writers.

## Outcome

- One `TileCsvWriter` class in `Common`.
- Region and route CSV-writing paths both use it.
- Output bytes byte-for-byte identical to pre-refactor.
- 37 unit + 5 smoke tests stay green.

## Scope

### Included
- Add `TileCsvWriter` with `Task WriteAsync(string path, IEnumerable<TileRecord> tiles, CancellationToken ct)`.
- Add a `TileRecord` record (or use an existing minimal DTO).
- Replace the duplicate writers in `RegionService` and `RouteProcessingService` (or its post-C11 `RouteCsvWriter` collaborator).
- Unit-test the writer.

### Excluded
- Changing the CSV header or column order.
- Changing the numeric format (`F6` stays).

## Acceptance Criteria

**AC-1: Single writer used by both consumers**
Given the post-refactor source
When grepped for the CSV header `latitude,longitude,file_path`
Then matches are confined to `TileCsvWriter`.

**AC-2: Output bytes identical**
Given the existing region/route scenarios
When the post-refactor code runs
Then the produced CSV files are byte-for-byte identical.

**AC-3: Tests stay green**
Given the post-refactor build
When `scripts/run-tests.sh --smoke` runs
Then all 37 unit + 5 smoke scenarios pass.

## Constraints

- CSV header, column order, numeric format unchanged.

## Risks & Mitigation

**Risk 1: line-ending drift on Windows**
- *Risk*: a refactor may swap `\n` for `\r\n` and break byte-identical comparison.
- *Mitigation*: use `StreamWriter` with `NewLine = "\n"` (matching current behavior).

Full change entry: `_docs/04_refactoring/03-code-quality-refactoring/list-of-changes.md` (C15).
