# 엑셀 설비대장을 CMMS로 옮기기 전에 정리할 곳을 찾아 목록으로 만든다.
# 사용법: python check_ledger.py <설비대장.xlsx>  → 같은 폴더에 '정리필요목록.xlsx'
# 설비대장의 첫 행은 머리글이어야 한다. 열 이름은 아래 설정에 맞춘다.
import sys, re, time
from datetime import datetime, date
from pathlib import Path
from collections import Counter
from openpyxl import load_workbook, Workbook

# 열 이름 설정: 왼쪽은 스크립트가 쓰는 이름, 오른쪽은 설비대장에서 그 열로 인정할 머리글들.
# 회사 설비대장의 머리글이 다르면 오른쪽 목록에 추가한다.
COLUMNS = {
    '설비번호': ['설비번호'], '설비명': ['설비명'], '설비유형': ['설비유형'], '위치': ['위치'],
    '상위설비': ['상위설비'], '용량': ['용량', '용량(kW)'], '설치일': ['설치일'], '중요도': ['중요도'],
    '상태': ['상태'], '비고': ['비고'],
}
REQUIRED = ['설비번호', '설비명', '설비유형', '위치', '설치일', '중요도']   # 비면 안 되는 열
TYPE_STANDARD = ['펌프', '모터', '컴프레서', '컨베이어', '팬']             # 설비유형 표준 이름
CAPACITY = re.compile(r'^\d+(\.\d+)?kW$')                                # 용량 표기 규칙
STATUS_WORDS = ['폐기', '불용', '이설', '매각']                           # 비고에 숨은 상태

def main(path: Path) -> None:
    start = time.perf_counter()
    try:
        ws = load_workbook(path, data_only=True).worksheets[0]
    except Exception:
        sys.exit('파일을 열 수 없습니다. .xlsx 파일인지, 암호·DRM이 걸려 있지 않은지 확인하세요.')
    rows = list(ws.iter_rows(values_only=True)) or [()]
    head = [str(h).strip() if h is not None else '' for h in rows[0]]
    col = {}
    for key, names in COLUMNS.items():
        hit = [head.index(n) for n in names if n in head]
        if hit: col[key] = hit[0]
    missing = [k for k in COLUMNS if k not in col and k not in ('상태',)]
    if missing:
        sys.exit(f'설비대장에서 찾지 못한 열: {", ".join(missing)} (머리글이 첫 번째 시트의 1행에 있는지 확인하고, 이름이 다르면 스크립트 위쪽 COLUMNS에 추가하세요)')
    # 빈 행은 검사하지 않는다(템플릿의 빈 입력 칸 등)
    data = [(n, r) for n, r in enumerate(rows[1:], start=2) if any(v not in (None, '') for v in r)]
    key = lambda v: str(v).strip().upper()   # 'P-001 '·'p-001'·숫자 1001과 문자 '1001'을 같은 번호로 본다
    ids = Counter(key(r[col['설비번호']]) for _, r in data if r[col['설비번호']])
    id_set = set(ids)
    found = []  # (행, 설비번호, 항목, 현재값, 제안)
    for n, r in data:
        no = r[col['설비번호']]
        for name in REQUIRED:
            if r[col[name]] is None or str(r[col[name]]).strip() == '':
                found.append((n, no, '필수값 누락', name, f'{name} 값을 채운다'))
        if no and ids[key(no)] > 1:
            found.append((n, no, '설비번호 중복', no, '한쪽 번호를 새로 부여한다'))
        t = r[col['설비유형']]
        if t and t not in TYPE_STANDARD:
            found.append((n, no, '유형 표기 다름', t, '표준 이름 중 하나로 바꾼다'))
        d = r[col['설치일']]
        if d not in (None, '') and not isinstance(d, (datetime, date)):
            found.append((n, no, '날짜 형식', str(d), '날짜 칸으로 다시 입력한다(예: 2019-03-01)'))
        cap = r[col['용량']]
        # 머리글에 단위가 있으면(용량(kW)) 숫자만 있어도 된다
        unit_in_header = head[col['용량']].endswith('(kW)') and isinstance(cap, (int, float))
        if cap not in (None, '') and not unit_in_header and not CAPACITY.match(str(cap)):
            hint = '숫자로 다시 입력한다(머리글에 단위가 있음)' if head[col['용량']].endswith('(kW)') else 'kW로 통일한다(예: 7.5kW)'
            found.append((n, no, '용량 단위', str(cap), hint))
        parent = r[col['상위설비']]
        if parent and key(parent) not in id_set:
            found.append((n, no, '상위설비 없음', parent, '상위 설비번호를 확인한다'))
        memo = str(r[col['비고']] or '')
        if '상태' not in col and any(w in memo for w in STATUS_WORDS):   # 상태 열이 있으면 이 검사는 하지 않는다
            found.append((n, no, '상태가 비고에 있음', memo, "'상태' 열을 만들어 옮긴다"))

    out = Workbook(); o = out.active; o.title = '정리필요목록'
    o.append(['행', '설비번호', '항목', '현재값', '제안'])
    for f in found: o.append(list(f))
    for c, w in zip('ABCDE', [6, 12, 18, 22, 36]): o.column_dimensions[c].width = w
    try:
        out.save(path.with_name('정리필요목록.xlsx'))
    except PermissionError:
        sys.exit('정리필요목록.xlsx를 저장하지 못했습니다. 엑셀에서 열려 있으면 닫고 다시 실행하세요.')
    sec = time.perf_counter() - start
    print(f'설비 {len(data)}대 검사, 정리할 곳 {len(found)}건, {sec:.2f}초')
    for k, v in Counter(f[2] for f in found).most_common():
        print(f'  {k}: {v}건')

if __name__ == '__main__':
    if len(sys.argv) < 2 or not Path(sys.argv[1]).is_file():
        sys.exit('사용법: python check_ledger.py <설비대장.xlsx>  (파일 이름·경로를 확인하세요)')
    if Path(sys.argv[1]).name == '정리필요목록.xlsx':
        sys.exit('결과 파일(정리필요목록.xlsx)이 아니라 설비대장 파일을 지정하세요.')
    main(Path(sys.argv[1]))
