-- =============================================================================
-- MariaDB 데이터베이스 스키마 생성 SQL (오류 수정 완)
-- Aislo (아이슬로) — AI + Slotti 산림 도로 설계 자동화 시스템
--
-- DB 명: aislo_db
-- DBMS: MariaDB 10.6+
-- 실행 순서:
-- 1. DATABASE 생성 (aislo_db)
-- 2. CREATE TABLE (각 테이블)
-- 3. CREATE INDEX (인덱싱)
-- 4. ALTER TABLE ADD CONSTRAINT (외래키)
-- =============================================================================

-- 0. 데이터베이스 생성
-- =============================================================================
CREATE DATABASE IF NOT EXISTS aislo_db
  CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

USE aislo_db;


-- 1. 테이블 생성 (순서 중요: FK 참조 테이블이 먼저 생성되어야 함)
-- =============================================================================

/* --------- 1-1. 사용자 & 인증 그룹 --------- */

-- users: 시스템 사용자
CREATE TABLE IF NOT EXISTS users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) NOT NULL UNIQUE,
  password_hash VARCHAR(255) NOT NULL,
  name VARCHAR(255) NOT NULL,
  position VARCHAR(100),                    -- 직급 (과장, 대리, 사원)
  department VARCHAR(100),                  -- 부서명 (설계팀, 영업팀)
  phone VARCHAR(20),                        -- 연락처 (010-1234-5678)
  company_id INT,                           -- 소속 회사 (FK는 later)
  role ENUM('SYSTEM_ADMIN', 'MASTER', 'MEMBER') NOT NULL DEFAULT 'MEMBER',
  is_master BOOLEAN NOT NULL DEFAULT FALSE,
  status ENUM('PENDING_EMAIL', 'PENDING', 'ACTIVE', 'INACTIVE', 'REJECTED') NOT NULL DEFAULT 'PENDING_EMAIL',
  last_email_verified_at TIMESTAMP NULL,
  login_failures INT NOT NULL DEFAULT 0,
  last_failed_at TIMESTAMP NULL,
  account_locked_until TIMESTAMP NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP NULL DEFAULT NULL    -- 소프트 삭제
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- companies: 회사 정보
CREATE TABLE IF NOT EXISTS companies (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL UNIQUE,
  business_registration_number VARCHAR(20) UNIQUE,  -- 사업자등록번호
  business_address VARCHAR(255),                     -- 사업장 주소
  business_owner VARCHAR(100),                       -- 사업주 이름
  business_status VARCHAR(50),                       -- 기업 상태 (활동중, 폐업, 휴업)
  created_by INT,                           -- 생성자 (FK는 later)
  company_code VARCHAR(20) NOT NULL UNIQUE,
  phone_number VARCHAR(20),
  representative_name VARCHAR(100),
  master_user_id INT NULL,
  status ENUM('ACTIVE', 'INACTIVE', 'SUSPENDED') NOT NULL DEFAULT 'ACTIVE',
  subscription_status ENUM('FREE', 'TRIAL', 'PAID', 'EXPIRED') NOT NULL DEFAULT 'FREE',
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- sessions: Secure HttpOnly 쿠키의 서버 측 세션 저장소
CREATE TABLE IF NOT EXISTS sessions (
  id VARCHAR(255) PRIMARY KEY,
  user_id INT NOT NULL,
  user_agent_hash CHAR(64) NOT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  expires_at TIMESTAMP NOT NULL,
  last_activity_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_sessions_user_id (user_id),
  INDEX idx_sessions_expires_at (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- email_otps: 목적별 일회용 이메일 인증 코드
CREATE TABLE IF NOT EXISTS email_otps (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  purpose ENUM('REGISTER', 'LOGIN', 'REVERIFY', 'PASSWORD_CHANGE') NOT NULL,
  otp_hash VARCHAR(255) NOT NULL,
  expires_at TIMESTAMP NOT NULL,
  consumed_at TIMESTAMP NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_email_otps_user_purpose (user_id, purpose, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS trusted_devices (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  user_agent_hash CHAR(64) NOT NULL,
  verified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  last_used_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uq_trusted_devices_user_agent (user_id, user_agent_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS join_requests (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  company_id INT NOT NULL,
  status ENUM('PENDING', 'APPROVED', 'REJECTED') NOT NULL DEFAULT 'PENDING',
  requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  reviewed_at TIMESTAMP NULL,
  reviewed_by INT NULL,
  review_comment TEXT,
  INDEX idx_join_requests_company_status (company_id, status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS login_logs (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NULL,
  email VARCHAR(255) NOT NULL,
  status ENUM('SUCCESS', 'FAILURE') NOT NULL,
  failure_reason VARCHAR(100),
  user_agent TEXT,
  login_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_login_logs_user_id (user_id),
  INDEX idx_login_logs_login_at (login_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS activity_logs (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  action_type VARCHAR(100) NOT NULL,
  action_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  resource_id VARCHAR(255),
  status ENUM('SUCCESS', 'FAILURE') NOT NULL DEFAULT 'SUCCESS',
  details JSON,
  INDEX idx_activity_logs_user_at (user_id, action_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS system_admin_logs (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  admin_user_id INT NOT NULL,
  action_type VARCHAR(100) NOT NULL,
  target_id VARCHAR(255),
  action_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  details JSON,
  INDEX idx_system_admin_logs_admin_at (admin_user_id, action_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS support_requests (
  id INT AUTO_INCREMENT PRIMARY KEY,
  company_id INT NULL,
  user_email VARCHAR(255) NOT NULL,
  subject VARCHAR(255) NOT NULL,
  message TEXT NOT NULL,
  status ENUM('NEW', 'REVIEWED', 'RESOLVED', 'DELETED') NOT NULL DEFAULT 'NEW',
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  reviewed_at TIMESTAMP NULL,
  reviewed_by INT NULL,
  review_notes TEXT,
  INDEX idx_support_requests_status_at (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS user_consents (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  terms_version VARCHAR(20) NOT NULL,
  terms_agreed BOOLEAN NOT NULL,
  privacy_agreed BOOLEAN NOT NULL,
  marketing_agreed BOOLEAN NOT NULL DEFAULT FALSE,
  agreed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uq_user_consents_version (user_id, terms_version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-2. 프로젝트 그룹 --------- */

-- projects: 설계 프로젝트
CREATE TABLE IF NOT EXISTS projects (
  id CHAR(36) PRIMARY KEY COMMENT 'UUID',
  user_id INT NOT NULL,                     -- 프로젝트 소유자 (FK는 later)
  company_id INT NOT NULL,                  -- 소속 회사 (FK는 later)
  name VARCHAR(255) NOT NULL,
  region VARCHAR(100),                      -- 지역명 (울진군 금강송면)
  road_type VARCHAR(100),                   -- 임도 종류 (간선임도, 지선임도, 산불진화임도, 계류보전)
  project_year INT,                         -- 사업 연도
  estimated_length_m FLOAT,                 -- 추정 연장 (m)
  memo TEXT,
  status VARCHAR(50) DEFAULT 'NEW',         -- NEW, FILE_UPLOADED, WF1_ANALYZING, ..., DONE
  crs_epsg INT DEFAULT 5178,                -- 좌표계 (한국 표준)
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  deleted_at TIMESTAMP NULL DEFAULT NULL,
  storage_path VARCHAR(500)                 -- storage/회사/사용자/project_uuid
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- project_versions: 프로젝트 버전 이력
CREATE TABLE IF NOT EXISTS project_versions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  version_num INT NOT NULL,
  snapshot_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  status VARCHAR(50),
  data JSON,                                -- 설계 데이터 스냅샷
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-3. 파일 & 입력 데이터 그룹 --------- */

-- input_files: 사용자 업로드 원본 파일
CREATE TABLE IF NOT EXISTS input_files (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  file_type VARCHAR(50),                    -- las, tif, tfw, prj, dxf, dwg, other
  original_filename VARCHAR(255) NOT NULL,
  raw_file_path VARCHAR(500) NOT NULL,      -- storage/.../B03_FileInput/input/...
  file_size_mb FLOAT,
  upload_by INT,                            -- FK는 later
  upload_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  crs_epsg INT,
  metadata JSON,                            -- resolution_m, data_range, ...
  status VARCHAR(50) DEFAULT 'UPLOADED'     -- UPLOADED, PROCESSED, ARCHIVED
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- processed_point_cloud: 변환된 포인트클라우드 데이터
CREATE TABLE IF NOT EXISTS processed_point_cloud (
  id INT AUTO_INCREMENT PRIMARY KEY,
  input_file_id INT NOT NULL,               -- FK는 later
  project_id CHAR(36) NOT NULL,             -- FK는 later
  process_type VARCHAR(50),                 -- filtered, sampled, classified
  processed_file_path VARCHAR(500),         -- storage/.../B04_wf1_Surface/processed/...
  converted_format VARCHAR(50),             -- ply, laz, las 등
  converted_file_path VARCHAR(500),         -- storage/.../B04_wf1_Surface/processed/...
  point_count INT,
  min_z FLOAT,
  max_z FLOAT,
  mean_z FLOAT,
  x_min FLOAT,
  x_max FLOAT,
  y_min FLOAT,
  y_max FLOAT,
  density_per_sqm FLOAT,
  classification_summary JSON,              -- ground, vegetation, building, ...
  processing_params JSON,
  processed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  status VARCHAR(50) DEFAULT 'PROCESSING'   -- PROCESSING, COMPLETE, FAILED
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-4. 지표면 & 분석 결과 그룹 --------- */

-- surface_models: WF1 지표면 분석 결과
CREATE TABLE IF NOT EXISTS surface_models (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  model_type VARCHAR(50),                   -- dem_grid, tin, mesh_triangulated, contour_lines
  source_file_id INT,                       -- FK는 later
  processed_cloud_id INT,                   -- FK는 later
  status VARCHAR(50) DEFAULT 'PROCESSING',  -- PROCESSING, COMPLETE, FAILED
  crs_epsg INT,
  resolution_m FLOAT,
  model_file_path VARCHAR(500),             -- storage/.../B04_wf1_Surface/models/...
  generation_params JSON,                   -- filter_type, algorithm, params
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  completed_at TIMESTAMP NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- terrain_layers: WF1 지형 레이어
CREATE TABLE IF NOT EXISTS terrain_layers (
  id INT AUTO_INCREMENT PRIMARY KEY,
  surface_model_id INT NOT NULL,            -- FK는 later
  layer_name VARCHAR(100),                  -- 지표, 제1층, 제2층
  geometry_type VARCHAR(50),                -- POINTCLOUD, GRID, MESH, CONTOUR
  layer_file_path VARCHAR(500),             -- storage/.../B04_wf1_Surface/models/...
  file_format VARCHAR(50),                  -- geojson, geotiff, ply, obj
  file_size_mb FLOAT,
  statistics JSON,                          -- min_z, max_z, mean_slope, point_count
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-5. 경로 & 설계 그룹 --------- */

-- routes: WF2 경로 설계 결과
CREATE TABLE IF NOT EXISTS routes (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  surface_model_id INT,                     -- FK는 later
  status VARCHAR(50) DEFAULT 'DRAFT',       -- DRAFT, CONFIRMED, ARCHIVED
  start_chainage_m FLOAT,
  end_chainage_m FLOAT,
  total_length_m FLOAT,
  grade_percent JSON,                       -- [2.5, 1.8, 3.2, ...] 각 구간 경사도
  constraints JSON,                         -- max_grade, min_radius, avoidance_zones
  algorithm_params JSON,                    -- cost_weights, ...
  route_data_path VARCHAR(500),             -- storage/.../B05_wf2_Route/route/...
  computed_at TIMESTAMP NULL DEFAULT NULL,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- route_points: 웹 렌더링용 경로 포인트 샘플
CREATE TABLE IF NOT EXISTS route_points (
  id INT AUTO_INCREMENT PRIMARY KEY,
  route_id INT NOT NULL,                    -- FK는 later
  chainage_m FLOAT,
  elevation_m FLOAT,
  slope_percent FLOAT,
  sequence_num INT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- route_statistics: 경로 통계
CREATE TABLE IF NOT EXISTS route_statistics (
  id INT AUTO_INCREMENT PRIMARY KEY,
  route_id INT NOT NULL,                    -- FK는 later
  min_slope FLOAT,
  max_slope FLOAT,
  mean_slope FLOAT,
  cut_volume_m3 FLOAT,
  fill_volume_m3 FLOAT,
  tree_cutting_volume FLOAT,
  cost_score FLOAT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-6. 종단면 & 횡단면 그룹 --------- */

-- longitudinal_sections: WF3 종단면
CREATE TABLE IF NOT EXISTS longitudinal_sections (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  route_id INT NOT NULL,                    -- FK는 later
  computed_at TIMESTAMP NULL DEFAULT NULL,
  data JSON,                                -- chainages, elevations, grades, design_elevations
  longitudinal_file_path VARCHAR(500),      -- storage/.../B06_wf3_ProfileCross/longitudinal/...
  status VARCHAR(50) DEFAULT 'DRAFT'        -- DRAFT, CONFIRMED
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- cross_sections: WF3 횡단면
CREATE TABLE IF NOT EXISTS cross_sections (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  route_id INT NOT NULL,                    -- FK는 later
  chainage_m FLOAT,
  sequence_num INT,
  data JSON,                                -- left_slope, right_slope, width_m, cut_volume_m3, fill_volume_m3, structures, notes
  cross_section_file_path VARCHAR(500),     -- storage/.../B06_wf3_ProfileCross/cross_sections/...
  status VARCHAR(50) DEFAULT 'DRAFT'        -- DRAFT, CONFIRMED
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-7. 설계 & 구조물 그룹 --------- */

-- structures: WF4 상세 설계 - 구조물
CREATE TABLE IF NOT EXISTS structures (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  cross_section_id INT,                     -- FK는 later
  structure_type VARCHAR(100),              -- 낙석방지책, 돌붙임, 계간수로, 낙차공
  chainage_m FLOAT,
  location VARCHAR(50),                     -- LEFT, CENTER, RIGHT
  length_m FLOAT,
  width_m FLOAT,
  height_m FLOAT,
  material VARCHAR(100),                    -- 강재, 콘크리트, 목재, 돌
  quantity INT,
  unit_price FLOAT,
  design_notes JSON,
  structure_data_path VARCHAR(500),         -- storage/.../B07_wf4_DesignDetail/structures/...
  last_modified_by INT,                     -- FK는 later
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-8. 수량 산출 그룹 --------- */

-- quantity_items: WF5 수량 산출
CREATE TABLE IF NOT EXISTS quantity_items (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  category VARCHAR(100),                    -- 토공, 구조물, 포장, 배수, 녹화, 안전시설
  item_name VARCHAR(255),
  unit VARCHAR(50),                         -- m3, 개, m, m2
  quantity_design FLOAT,
  quantity_actual FLOAT,
  unit_price FLOAT,
  total_price FLOAT,                        -- quantity_actual × unit_price
  standard_reference VARCHAR(255),
  computed_at TIMESTAMP NULL DEFAULT NULL,
  quantity_data_path VARCHAR(500),          -- storage/.../B08_wf5_Quantity/quantities/...
  data JSON                                 -- formula, source, ...
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-9. 산출물 & 문서 그룹 --------- */

-- outputs: WF6 최종 산출물 세트
CREATE TABLE IF NOT EXISTS outputs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  output_type VARCHAR(100),                 -- estimation_excel, drawing_dxf, report_pdf, all_bundle
  status VARCHAR(50) DEFAULT 'GENERATING',  -- GENERATING, COMPLETE, FAILED
  generated_by INT,                         -- FK는 later
  generated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  version INT DEFAULT 1,
  outputs_directory_path VARCHAR(500),      -- storage/.../B09_wf6_Estimation/v1/
  metadata JSON                             -- template_used, company_name, project_name, total_cost, generation_time_sec
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- output_files: 최종 산출 파일
CREATE TABLE IF NOT EXISTS output_files (
  id INT AUTO_INCREMENT PRIMARY KEY,
  output_id INT NOT NULL,                   -- FK는 later
  file_type VARCHAR(50),                    -- xlsx, pdf, dxf, dwg, json, zip
  original_filename VARCHAR(255),
  output_file_path VARCHAR(500),            -- storage/.../B09_wf6_Estimation/v1/...
  file_size_mb FLOAT,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  download_count INT DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


/* --------- 1-10. 변경 이력 & 감시 그룹 --------- */

-- audit_logs: 보안 감시 로그
CREATE TABLE IF NOT EXISTS audit_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT,                              -- FK는 later
  action VARCHAR(100),                      -- CREATE, READ, UPDATE, DELETE, EXPORT, DOWNLOAD
  entity_type VARCHAR(100),                 -- projects, routes, structures, outputs
  entity_id VARCHAR(36),
  timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  ip_address VARCHAR(45),
  details JSON                              -- old_value, new_value, reason
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- change_logs: 설계 변경 이력 
-- [★오류 수정]: 외래키 ON DELETE SET NULL 조건을 위해 changed_by 컬럼을 NULL 가능으로 변경
CREATE TABLE IF NOT EXISTS change_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  project_id CHAR(36) NOT NULL,             -- FK는 later
  changed_by INT NULL,                      -- NOT NULL 에서 NULL로 변경 완료
  changed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  entity_type VARCHAR(100),                 -- routes, cross_sections, structures, quantity_items
  entity_id INT,
  old_value JSON,
  new_value JSON,
  reason VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


-- =============================================================================
-- 2. 외래키(FK) 제약조건 추가
-- =============================================================================

ALTER TABLE users
  ADD CONSTRAINT fk_users_company_id
  FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE SET NULL;

ALTER TABLE companies
  ADD CONSTRAINT fk_companies_created_by
  FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
  ADD CONSTRAINT fk_companies_master_user_id
  FOREIGN KEY (master_user_id) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE sessions
  ADD CONSTRAINT fk_sessions_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

ALTER TABLE email_otps
  ADD CONSTRAINT fk_email_otps_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

ALTER TABLE trusted_devices
  ADD CONSTRAINT fk_trusted_devices_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

ALTER TABLE join_requests
  ADD CONSTRAINT fk_join_requests_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_join_requests_company_id FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_join_requests_reviewed_by FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE login_logs
  ADD CONSTRAINT fk_login_logs_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE activity_logs
  ADD CONSTRAINT fk_activity_logs_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

ALTER TABLE system_admin_logs
  ADD CONSTRAINT fk_system_admin_logs_admin_user_id FOREIGN KEY (admin_user_id) REFERENCES users(id) ON DELETE CASCADE;

ALTER TABLE support_requests
  ADD CONSTRAINT fk_support_requests_company_id FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE SET NULL,
  ADD CONSTRAINT fk_support_requests_reviewed_by FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE user_consents
  ADD CONSTRAINT fk_user_consents_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

ALTER TABLE projects
  ADD CONSTRAINT fk_projects_user_id
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT,
  ADD CONSTRAINT fk_projects_company_id
  FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE RESTRICT;

ALTER TABLE project_versions
  ADD CONSTRAINT fk_project_versions_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

ALTER TABLE input_files
  ADD CONSTRAINT fk_input_files_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_input_files_upload_by
  FOREIGN KEY (upload_by) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE processed_point_cloud
  ADD CONSTRAINT fk_processed_point_cloud_input_file_id
  FOREIGN KEY (input_file_id) REFERENCES input_files(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_processed_point_cloud_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

ALTER TABLE surface_models
  ADD CONSTRAINT fk_surface_models_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_surface_models_source_file_id
  FOREIGN KEY (source_file_id) REFERENCES input_files(id) ON DELETE SET NULL,
  ADD CONSTRAINT fk_surface_models_processed_cloud_id
  FOREIGN KEY (processed_cloud_id) REFERENCES processed_point_cloud(id) ON DELETE SET NULL;

ALTER TABLE terrain_layers
  ADD CONSTRAINT fk_terrain_layers_surface_model_id
  FOREIGN KEY (surface_model_id) REFERENCES surface_models(id) ON DELETE CASCADE;

ALTER TABLE routes
  ADD CONSTRAINT fk_routes_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_routes_surface_model_id
  FOREIGN KEY (surface_model_id) REFERENCES surface_models(id) ON DELETE SET NULL;

ALTER TABLE route_points
  ADD CONSTRAINT fk_route_points_route_id
  FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE;

ALTER TABLE route_statistics
  ADD CONSTRAINT fk_route_statistics_route_id
  FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE;

ALTER TABLE longitudinal_sections
  ADD CONSTRAINT fk_longitudinal_sections_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_longitudinal_sections_route_id
  FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE;

ALTER TABLE cross_sections
  ADD CONSTRAINT fk_cross_sections_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_cross_sections_route_id
  FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE;

ALTER TABLE structures
  ADD CONSTRAINT fk_structures_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_structures_cross_section_id
  FOREIGN KEY (cross_section_id) REFERENCES cross_sections(id) ON DELETE SET NULL,
  ADD CONSTRAINT fk_structures_last_modified_by
  FOREIGN KEY (last_modified_by) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE quantity_items
  ADD CONSTRAINT fk_quantity_items_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

ALTER TABLE outputs
  ADD CONSTRAINT fk_outputs_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_outputs_generated_by
  FOREIGN KEY (generated_by) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE output_files
  ADD CONSTRAINT fk_output_files_output_id
  FOREIGN KEY (output_id) REFERENCES outputs(id) ON DELETE CASCADE;

ALTER TABLE audit_logs
  ADD CONSTRAINT fk_audit_logs_user_id
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;

ALTER TABLE change_logs
  ADD CONSTRAINT fk_change_logs_project_id
  FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
  ADD CONSTRAINT fk_change_logs_changed_by
  FOREIGN KEY (changed_by) REFERENCES users(id) ON DELETE SET NULL;


-- =============================================================================
-- 3. 인덱스 생성 (성능 최적화)
-- =============================================================================

-- 사용자/회사 인덱싱
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_company_id ON users(company_id);
CREATE INDEX idx_companies_name ON companies(name);

-- 프로젝트 인덱싱
CREATE INDEX idx_projects_user_id ON projects(user_id);
CREATE INDEX idx_projects_company_id ON projects(company_id);
CREATE INDEX idx_projects_status ON projects(status);
CREATE INDEX idx_projects_created_at ON projects(created_at DESC);
CREATE INDEX idx_projects_storage_path ON projects(storage_path);

-- 파일 인덱싱
CREATE INDEX idx_input_files_project_id ON input_files(project_id);
CREATE INDEX idx_input_files_file_type ON input_files(file_type);
CREATE INDEX idx_input_files_raw_file_path ON input_files(raw_file_path);
CREATE INDEX idx_processed_point_cloud_input_file_id ON processed_point_cloud(input_file_id);
CREATE INDEX idx_processed_point_cloud_project_id ON processed_point_cloud(project_id);
CREATE INDEX idx_processed_point_cloud_converted_file_path ON processed_point_cloud(converted_file_path);

-- 지표면 모델 인덱싱
CREATE INDEX idx_surface_models_project_id ON surface_models(project_id);
CREATE INDEX idx_surface_models_status ON surface_models(status);
CREATE INDEX idx_surface_models_model_file_path ON surface_models(model_file_path);
CREATE INDEX idx_terrain_layers_surface_model_id ON terrain_layers(surface_model_id);
CREATE INDEX idx_terrain_layers_layer_file_path ON terrain_layers(layer_file_path);

-- 경로 인덱싱
CREATE INDEX idx_routes_project_id ON routes(project_id);
CREATE INDEX idx_routes_status ON routes(status);
CREATE INDEX idx_routes_route_data_path ON routes(route_data_path);
CREATE INDEX idx_route_points_route_id ON route_points(route_id);
CREATE INDEX idx_route_statistics_route_id ON route_statistics(route_id);

-- 단면 인덱싱
CREATE INDEX idx_longitudinal_sections_project_id ON longitudinal_sections(project_id);
CREATE INDEX idx_longitudinal_sections_route_id ON longitudinal_sections(route_id);
CREATE INDEX idx_longitudinal_sections_file_path ON longitudinal_sections(longitudinal_file_path);
CREATE INDEX idx_cross_sections_project_id ON cross_sections(project_id);
CREATE INDEX idx_cross_sections_route_id ON cross_sections(route_id);
CREATE INDEX idx_cross_sections_chainage_m ON cross_sections(chainage_m);
CREATE INDEX idx_cross_sections_file_path ON cross_sections(cross_section_file_path);

-- 구조물/수량 인덱싱
CREATE INDEX idx_structures_project_id ON structures(project_id);
CREATE INDEX idx_structures_cross_section_id ON structures(cross_section_id);
CREATE INDEX idx_structures_file_path ON structures(structure_data_path);
CREATE INDEX idx_quantity_items_project_id ON quantity_items(project_id);
CREATE INDEX idx_quantity_items_category ON quantity_items(category);
CREATE INDEX idx_quantity_items_file_path ON quantity_items(quantity_data_path);

-- 산출물 인덱싱
CREATE INDEX idx_outputs_project_id ON outputs(project_id);
CREATE INDEX INDEX idx_outputs_version ON outputs(version);
CREATE INDEX idx_outputs_directory_path ON outputs(outputs_directory_path);
CREATE INDEX idx_output_files_output_id ON output_files(output_id);
CREATE INDEX idx_output_files_output_file_path ON output_files(output_file_path);

-- 감시/이력 인덱싱
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_timestamp ON audit_logs(timestamp DESC);
CREATE INDEX idx_audit_logs_entity_type ON audit_logs(entity_type);
CREATE INDEX idx_change_logs_project_id ON change_logs(project_id);
CREATE INDEX idx_change_logs_changed_by ON change_logs(changed_by);
CREATE INDEX idx_change_logs_changed_at ON change_logs(changed_at DESC);

-- =============================================================================
-- 5. 생성 완료 메시지
-- =============================================================================

-- 모든 테이블과 인덱스가 정상적으로 생성되었으면 아래의 SELECT를 실행하여 확인할 수 있습니다:
-- SELECT table_name FROM information_schema.tables WHERE table_schema='public';
-- SELECT indexname FROM pg_indexes WHERE schemaname='public';
